# plot.py Test/demo program for micro-gui plot. Cross-patform, # but requires a large enough display. # Tested on Adafruit ssd1351-based OLED displays: # Adafruit 1.5" 128*128 OLED display: https://www.adafruit.com/product/1431 # Adafruit 1.27" 128*96 display https://www.adafruit.com/product/1673 # Released under the MIT License (MIT). See LICENSE. # Copyright (c) 2021 Peter Hinch # Create SSD instance. Must be done first because of RAM use. import hardware_setup import cmath import math import uasyncio as asyncio from collections import OrderedDict from gui.core.writer import Writer, CWriter from gui.core.ugui import Screen, ssd from gui.widgets.graph import PolarGraph, PolarCurve, CartesianGraph, Curve, TSequence from gui.widgets import Label, Button, CloseButton, Listbox # Fonts & colors import gui.fonts.arial10 as arial10 from gui.core.colors import * wri = CWriter(ssd, arial10, GREEN, BLACK, verbose=False) def fwdbutton(writer, row, col, cls_screen, text, color, *args, **kwargs): def fwd(button): Screen.change(cls_screen, args = args, kwargs = kwargs) Button(writer, row, col, callback = fwd, bgcolor = color, text = text, textcolor = BLACK, height = 20, width = 60) class EmptyScreen(Screen): def __init__(self): super().__init__() Label(wri, 2, 2, 'Test of overlay.') Label(wri, 20, 2, 'Check redraw of underlying screen.') CloseButton(wri) class CartesianScreen(Screen): def __init__(self): super().__init__() self.g = CartesianGraph(wri, 2, 2, yorigin = 2, fgcolor=GREEN, gridcolor=LIGHTGREEN) # Asymmetric y axis Label(wri, 100, 2, 'Asymmetric axes.') fwdbutton(wri, 30, 130, EmptyScreen, 'Forward', GREEN) CloseButton(wri) # At this point in time the Screen and graph have been constructed but # not rendered. If we drew the curves now they would be overwritten by # the graph # Now the graph has been drawn and we can overlay it with graphics primitives. # This is not a uasyncio issue, but is the way Screen.change() is coded. def after_open(self): def populate_1(func): x = -1 while x < 1.01: yield x, func(x) # x, y x += 0.1 def populate_2(): x = -1 while x < 1.01: yield x, x**2 # x, y x += 0.1 Curve(self.g, YELLOW, populate_1(lambda x : x**3 + x**2 -x,)) # args demo Curve(self.g, RED, populate_2()) class PolarScreen(Screen): def __init__(self): super().__init__() self.g = PolarGraph(wri, 2, 2, fgcolor=GREEN, gridcolor=LIGHTGREEN) fwdbutton(wri, 30, 130, EmptyScreen, 'Forward', GREEN) CloseButton(wri) def after_open(self): # After graph has been drawn def populate(): def f(theta): return cmath.rect(math.sin(3 * theta), theta) # complex nmax = 150 for n in range(nmax + 1): yield f(2 * cmath.pi * n / nmax) # complex z PolarCurve(self.g, YELLOW, populate()) class Lissajous(Screen): def __init__(self): super().__init__() self.g = CartesianGraph(wri, 2, 2, fgcolor=GREEN, gridcolor=LIGHTGREEN) Label(wri, 100, 2, 'Lissajous figure.') fwdbutton(wri, 30, 130, EmptyScreen, 'Forward', GREEN) CloseButton(wri) def after_open(self): # After graph has been drawn def populate(): t = -math.pi while t <= math.pi: yield math.sin(t), math.cos(3*t) # x, y t += 0.1 Curve(self.g, YELLOW, populate()) class Lemniscate(Screen): def __init__(self): super().__init__() self.g = CartesianGraph(wri, 2, 2, height = 75, fgcolor=GREEN, gridcolor=LIGHTGREEN) Label(wri, 82, 2, 'To infinity and beyond...') fwdbutton(wri, 30, 130, EmptyScreen, 'Forward', GREEN) CloseButton(wri) def after_open(self): # After graph has been drawn def populate(): t = -math.pi while t <= math.pi + 0.1: x = 0.5*math.sqrt(2)*math.cos(t)/(math.sin(t)**2 + 1) y = math.sqrt(2)*math.cos(t)*math.sin(t)/(math.sin(t)**2 + 1) yield x, y t += 0.1 Curve(self.g, YELLOW, populate()) class PolarClip(Screen): def __init__(self): super().__init__() self.g = PolarGraph(wri, 2, 2, fgcolor=GREEN, gridcolor=LIGHTGREEN) Label(wri, 100, 2, 'Clipping of polar data.') fwdbutton(wri, 30, 130, EmptyScreen, 'Forward', GREEN) CloseButton(wri) def after_open(self): # After graph has been drawn def populate(rot): f = lambda theta : cmath.rect(1.15 * math.sin(5 * theta), theta) * rot # complex nmax = 150 for n in range(nmax + 1): yield f(2 * cmath.pi * n / nmax) # complex z PolarCurve(self.g, YELLOW, populate(1)) PolarCurve(self.g, RED, populate(cmath.rect(1, cmath.pi/5),)) class RTPolar(Screen): def __init__(self): super().__init__() self.g = PolarGraph(wri, 2, 2, fgcolor=GREEN, gridcolor=LIGHTGREEN) Label(wri, 100, 2, 'Realtime polar data.') fwdbutton(wri, 30, 130, EmptyScreen, 'Forward', GREEN) CloseButton(wri) def after_open(self): # After graph has been drawn self.reg_task(self.run(self.g), True) # Cancel on screen change async def run(self, g): await asyncio.sleep_ms(0) curvey = PolarCurve(g, YELLOW) curver = PolarCurve(g, RED) for x in range(100): curvey.point(cmath.rect(x/100, -x * cmath.pi/30)) curver.point(cmath.rect((100 - x)/100, -x * cmath.pi/30)) await asyncio.sleep_ms(60) class RTRect(Screen): def __init__(self): super().__init__() self.g = CartesianGraph(wri, 2, 2, fgcolor=GREEN, gridcolor=LIGHTGREEN) Label(wri, 100, 2, 'Realtime discontinuous data.') fwdbutton(wri, 30, 130, EmptyScreen, 'Forward', GREEN) CloseButton(wri) def after_open(self): # After graph has been drawn self.reg_task(self.run(self.g), True) # Cancel on screen change async def run(self, g): await asyncio.sleep_ms(0) curve = Curve(g, RED) x = -1 for _ in range(40): y = 0.1/x if abs(x) > 0.05 else None # Discontinuity curve.point(x, y) await asyncio.sleep_ms(100) x += 0.05 g.clear() curve = Curve(g, YELLOW) x = -1 for _ in range(40): y = -0.1/x if abs(x) > 0.05 else None # Discontinuity curve.point(x, y) await asyncio.sleep_ms(100) x += 0.05 class TSeq(Screen): def __init__(self): super().__init__() self.g = CartesianGraph(wri, 2, 2, xorigin = 10, fgcolor=GREEN, gridcolor=LIGHTGREEN, bdcolor=False) Label(wri, 100, 2, 'Time sequence.') fwdbutton(wri, 30, 130, EmptyScreen, 'Forward', GREEN) CloseButton(wri) def after_open(self): # After graph has been drawn self.reg_task(self.run(self.g), True) # Cancel on screen change async def run(self, g): await asyncio.sleep_ms(0) tsy = TSequence(g, YELLOW, 50) tsr = TSequence(g, RED, 50) t = 0 while True: g.show() # Redraw the empty graph tsy.add(0.9*math.sin(t/10)) tsr.add(0.4*math.cos(t/10)) # Plot the new curves await asyncio.sleep_ms(400) t += 1 class BaseScreen(Screen): def __init__(self): super().__init__() d = OrderedDict() d['Cartesian'] = CartesianScreen d['Polar'] = PolarScreen d['Lissajous'] = Lissajous d['Lemniscate'] = Lemniscate d['Polar clipping'] = PolarClip d['Realtime polar'] = RTPolar d['Realtime rect'] = RTRect d['Time sequence'] = TSeq row = 2 col = 2 Listbox(wri, row, col, callback=self.lbcb, args=(d,), elements = tuple(d.keys()), bdcolor = GREEN, bgcolor = DARKGREEN) CloseButton(wri) def lbcb(self, lb, d): Screen.change(d[lb.textvalue()]) def test(): if ssd.height < 128 or ssd.width < 200: print(' This test requires a display of at least 128x200 pixels.') else: print('Testing micro-gui...') Screen.change(BaseScreen) test()