Files
seweb/histgraph.py
Markus Zolliker 97c6aa1a87 major rework
- modified chartjs for better zoom on touchpad
- flexible number of charts
- handle bool as checkbox
- handle enum as select (pull down menu)
- disable swiper where not needed
and many more
2020-12-23 11:11:22 +01:00

172 lines
7.8 KiB
Python

import time
import sys
if sys.version_info >= (3,6):
Dict = dict
else:
from collections import OrderedDict as Dict
class PrettyFloat(float):
def __repr__(self):
return '%.15g' % self
def get_abs_time(*times):
now = int(time.time() + 0.999)
oneyear = 365 * 24 * 3600
return tuple(t + now if t < oneyear else t for t in times)
class ColorMap(object):
""""
ColorMap is using official CSS color names, with the exception of Green, as this
is defined differently with X11 colors than in SEA, and used heavily in config files.
Here Green is an alias to Lime (#00FF00) and MidGreen is #008000, which is called Green in CSS.
The function to_code is case insensitive and accepts also names with underscores.
The order is choosen by M. Zolliker for the SEA client, originally only the first 16 were used.
"""
hex_name = (
("#FFFFFF", "White"), ("#FF0000", "Red"), ("#00FF00", "Lime"), ("#0000FF", "Blue"), ("#FF00FF", "Magenta"),
("#FFFF00", "Yellow"), ("#00FFFF", "Cyan"), ("#000000", "Black"), ("#FFA500", "Orange"),
("#006400", "DarkGreen"), ("#9400D3", "DarkViolet"), ("#A52A2A", "Brown"), ("#87CEEB", "SkyBlue"),
("#808080", "Gray"), ("#FF69B4", "HotPink"), ("#FFFFE0", "LightYellow"), ("#00FF7F", "SpringGreen"),
("#000080", "Navy"), ("#1E90FF", "DodgerBlue"), ("#9ACD32", "YellowGreen"), ("#008B8B", "DarkCyan"),
("#808000", "Olive"), ("#DEB887", "BurlyWood"),
("#7B68EE", "MediumSlateBlue"), ("#483D8B", "DarkSlateBlue"), ("#98FB98", "PaleGreen"), ("#FF1493", "DeepPink"),
("#FF6347", "Tomato"), ("#32CD32", "LimeGreen"), ("#DDA0DD", "Plum"), ("#7FFF00", "Chartreuse"),
("#800080", "Purple"), ("#00CED1", "DarkTurquoise"), ("#8FBC8F", "DarkSeaGreen"), ("#4682B4", "SteelBlue"),
("#800000", "Maroon"),
("#3CB371", "MediumSeaGreen"), ("#FF4500", "OrangeRed"), ("#BA55D3", "MediumOrchid"),
("#2F4F4F", "DarkSlateGray"), ("#CD853F", "Peru"), ("#228B22", "ForestGreen"), ("#48D1CC", "MediumTurquoise"),
("#DC143C", "Crimson"),
("#D3D3D3", "LightGray"), ("#ADFF2F", "GreenYellow"), ("#7FFFD4", "Aquamarine"), ("#BC8F8F", "RosyBrown"),
("#20B2AA", "LightSeaGreen"), ("#C71585", "MediumVioletRed"), ("#F0E68C", "Khaki"),
("#6495ED", "CornflowerBlue"),
("#556B2F", "DarkOliveGreen"), ("#CD5C5C", "IndianRed "), ("#2E8B57", "SeaGreen"), ("#F08080", "LightCoral"),
("#8A2BE2", "BlueViolet"), ("#AFEEEE", "PaleTurquoise"), ("#4169E1", "RoyalBlue"), ("#0000CD", "MediumBlue"),
("#B8860B", "DarkGoldenRod"), ("#00BFFF", "DeepSkyBlue"), ("#FFC0CB", "Pink"), ("#4B0082", "Indigo "),
("#A0522D", "Sienna"),
("#FFD700", "Gold"), ("#F4A460", "SandyBrown"), ("#DAA520", "GoldenRod"), ("#DA70D6", "Orchid"),
("#E6E6FA", "Lavender"),
("#5F9EA0", "CadetBlue"), ("#D2691E", "Chocolate"), ("#66CDAA", "MediumAquaMarine"), ("#6B8E23", "OliveDrab"),
("#A9A9A9", "DarkGray"), ("#BDB76B", "DarkKhaki"), ("#696969", "DimGray"), ("#B0C4DE", "LightSteelBlue"),
("#191970", "MidnightBlue"), ("#FFE4C4", "Bisque"), ("#6A5ACD", "SlateBlue"), ("#EE82EE", "Violet"),
("#8B4513", "SaddleBrown"), ("#FF7F50", "Coral"), ("#008000", "MidGreen"), ("#DB7093", "PaleVioletRed"),
("#C0C0C0", "Silver"),
("#E0FFFF", "LightCyan"), ("#9370DB", "MediumPurple"), ("#FF8C00", "DarkOrange"),
("#00FA9A", "MediumSpringGreen"),
("#E9967A", "DarkSalmon"), ("#778899", "LightSlateGray"), ("#9932CC", "DarkOrchid"),
("#EEE8AA", "PaleGoldenRod"),
("#F8F8FF", "GhostWhite"), ("#FFA07A", "LightSalmon"), ("#ADD8E6", "LightBlue"), ("#D8BFD8", "Thistle"),
("#FFE4E1", "MistyRose"), ("#FFDEAD", "NavajoWhite"), ("#40E0D0", "Turquoise"), ("#90EE90", "LightGreen"),
("#B22222", "FireBrick"), ("#008080", "Teal"), ("#F0FFF0", "HoneyDew"), ("#FFFACD", "LemonChiffon"),
("#FFF5EE", "SeaShell"),
("#F5F5DC", "Beige"), ("#DCDCDC", "Gainsboro"), ("#FA8072", "Salmon"), ("#8B008B", "DarkMagenta"),
("#FFB6C1", "LightPink"),
("#708090", "SlateGray"), ("#87CEFA", "LightSkyBlue"), ("#FFEFD5", "PapayaWhip"), ("#D2B48C", "Tan"),
("#FFFFF0", "Ivory"),
("#F0FFFF", "Azure"), ("#F5DEB3", "Wheat"), ("#00008B", "DarkBlue"), ("#FFDAB9", "PeachPuff"),
("#8B0000", "DarkRed"),
("#FAF0E6", "Linen"), ("#B0E0E6", "PowderBlue"), ("#FFE4B5", "Moccasin"), ("#F5F5F5", "WhiteSmoke"),
("#FFF8DC", "Cornsilk"),
("#FFFAFA", "Snow"), ("#FFF0F5", "LavenderBlush"), ("#FFEBCD", "BlanchedAlmond"), ("#F0F8FF", "AliceBlue"),
("#FAEBD7", "AntiqueWhite"), ("#FDF5E6", "OldLace"), ("#FAFAD2", "LightGoldenRodYellow"),
("#F5FFFA", "MintCream"),
("#FFFAF0", "FloralWhite"), ("#7CFC00", "LawnGreen"), ("#663399", "RebeccaPurple"))
codes = {}
for i, pair in enumerate(hex_name):
codes[pair[0]] = i
low = pair[1].lower()
codes[low] = i
codes[low.replace("gray", "grey")] = i
codes["green"] = 2
codes["fuchsia"] = 4
codes["aqua"] = 6
@staticmethod
def to_code(colortext):
try:
return int(colortext)
except ValueError:
return ColorMap.codes.get(colortext.lower().replace("_", ""),-1)
@staticmethod
def check_hex(code):
if not code.startswith("#"):
return None
if len(code) == 4: # convert short code to long code
code = code[0:2] + code[1:3] + code[2:4] + code[3]
if len(code) != 7:
return None
try:
int(code[1:]) # we have a valid hex color code
return code
except ValueError:
return None
@staticmethod
def to_hex(code):
try:
return ColorMap.hex_name[code][0]
except IndexError:
return -1
def get_vars(main, time):
result = {}
time, = get_abs_time(time)
# get last value only
curves = main.get_curve_options((time, time))
for key, opts in curves.items():
if not opts.get('show', False):
continue
print(key, opts)
opts = dict(name=key, **opts)
unit = opts.pop('unit', '*')
tag = opts.pop('tag', unit)
if not tag in result:
result[tag] = dict(tag=tag, unit=unit, curves=Dict())
if 'label' not in opts:
opts['label'] = opts['name']
result[tag]["curves"][key] = opts
# determine colors
for _, curvegroup in result.items():
color_set = set()
auto_curves = []
curve_list = list(curvegroup["curves"].values())
curvegroup['curves'] = curve_list
for curve in curve_list:
col = curve.get("color", '').strip()
c = ColorMap.to_code(col)
if c < 0:
valid = ColorMap.check_hex(col)
if valid:
curve["original_color"] = col
curve["color"] = valid
else:
auto_curves.append(curve)
curve["original_color"] = col + "?"
else:
color_set.add(c)
curve["original_color"] = col
curve["color"] = ColorMap.to_hex(c)
c = 1 # omit white
for curve in auto_curves:
while c in color_set:
c += 1 # find unused color
curve["color"] = ColorMap.to_hex(c)
c += 1
return result
def get_curves(main, keys, timerange, cut_begin=True):
curves = main.get_curves(keys, get_abs_time(*timerange), maxpoints=500, cut_begin=cut_begin)
opts = main.get_curve_options(timerange, keys)
#if 'tt:target' in curves:
# print('---')
# print(curves['tt:target'].fmtm())
# print('TT', curves['tt:target'].for_json()[-5:])
return {k: c.for_json(opts.get(k, {}).get('period', 0)) for k, c in curves.items()}