diff --git a/README.md b/README.md index 7352cc8..3f8b28f 100644 --- a/README.md +++ b/README.md @@ -1798,4 +1798,42 @@ variable. ## 23.2 Use of graphics primitives - (ssd and display objects) +These notes are for those wishing to draw directly to the `Screen` instance. +This is done by providing the user `Screen` class with an `after_open()` method +which issues the display driver calls. + +The following code instantiates two classes: +```python +from hardware_setup import display, ssd +``` +The `ssd` object is an instance of the object defined in the display driver. It +is a requirement that this is a subclass of `framebuf.FrameBuffer`. Hence `ssd` +supports all the graphics primitives provided by `FrameBuffer`. These may be +used to draw on the `Screen`. + +The `display` object has an `ssd` bound variable which is a reference to the +`ssd` device. The `display` has methods with the same names and args as those +of `ssd`. These support greying out. So you can write (for example) +```python +display.rect(10, 10, 50, 50, RED) +``` +To render in the correct colors it is wise ensure that greying out is disabled +prior to calling `display` methods. This is done with +```python +display.usegrey(False) +``` +There is little point in issuing `display.rect` as it confers no advantage over +`ssd.rect`. However the `Display` class adds methods not currently available in +`framebuf`. These are listed below. + + * `circle(self, x0, y0, r, color, width =1)` Width specifies the line width. + * `fillcircle(self, x0, y0, r, color)` + * `clip_rect(self, x, y, w, h, color)` Rectangle with clipped corners. + * `fill_clip_rect(self, x, y, w, h, color)` + * `print_left(self, writer, x, y, txt, fgcolor=None, bgcolor=None, invert=False)` + * `print_centred(self, writer, x, y, text, fgcolor=None, bgcolor=None, invert=False)` + +Hopefully these are self explanatory. The `Display` methods use the `framebuf` +convention of `x, y` coordinates rather than the `row, col` system used by +micro-gui. + diff --git a/gui/core/ugui.py b/gui/core/ugui.py index 62a0684..2a982f8 100644 --- a/gui/core/ugui.py +++ b/gui/core/ugui.py @@ -65,15 +65,19 @@ class Display: self._down.close_func(self.do_down) self._is_grey = False # Not greyed-out - def print_centred(self, writer, x, y, text, fgcolor=None, bgcolor=None): + def print_centred(self, writer, x, y, text, fgcolor=None, bgcolor=None, invert=False): sl = writer.stringlen(text) writer.set_textpos(ssd, y - writer.height // 2, x - sl // 2) + if self._is_grey: + fgcolor = GREY writer.setcolor(fgcolor, bgcolor) - writer.printstring(text) + writer.printstring(text, invert) writer.setcolor() # Restore defaults def print_left(self, writer, x, y, txt, fgcolor=None, bgcolor=None, invert=False): writer.set_textpos(ssd, y, x) + if self._is_grey: + fgcolor = GREY writer.setcolor(fgcolor, bgcolor) writer.printstring(txt, invert) writer.setcolor() # Restore defaults @@ -230,7 +234,6 @@ class Screen: def show(cls, force): for obj in cls.current_screen.displaylist: if obj.visible: # In a buttonlist only show visible button - # pend signals an obect with changed contents if force or obj.draw: obj.draw_border() obj.show() diff --git a/gui/demos/plot.py b/gui/demos/plot.py index 0117743..60ac8f9 100644 --- a/gui/demos/plot.py +++ b/gui/demos/plot.py @@ -48,7 +48,7 @@ class CartesianScreen(Screen): self.g = CartesianGraph(wri, 2, 2, yorigin = 2, fgcolor=GREEN, gridcolor=LIGHTGREEN) # Asymmetric y axis Label(wri, 100, 2, 'Asymmetric axes.') - fwdbutton(wri, 2, 130, EmptyScreen, 'Forward', GREEN) + 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 @@ -76,7 +76,7 @@ class PolarScreen(Screen): def __init__(self): super().__init__() self.g = PolarGraph(wri, 2, 2, fgcolor=GREEN, gridcolor=LIGHTGREEN) - fwdbutton(wri, 2, 130, EmptyScreen, 'Forward', GREEN) + fwdbutton(wri, 30, 130, EmptyScreen, 'Forward', GREEN) CloseButton(wri) def after_open(self): # After graph has been drawn @@ -94,7 +94,7 @@ class Lissajous(Screen): super().__init__() self.g = CartesianGraph(wri, 2, 2, fgcolor=GREEN, gridcolor=LIGHTGREEN) Label(wri, 100, 2, 'Lissajous figure.') - fwdbutton(wri, 2, 130, EmptyScreen, 'Forward', GREEN) + fwdbutton(wri, 30, 130, EmptyScreen, 'Forward', GREEN) CloseButton(wri) def after_open(self): # After graph has been drawn @@ -111,7 +111,7 @@ class Lemniscate(Screen): super().__init__() self.g = CartesianGraph(wri, 2, 2, height = 75, fgcolor=GREEN, gridcolor=LIGHTGREEN) Label(wri, 82, 2, 'To infinity and beyond...') - fwdbutton(wri, 2, 130, EmptyScreen, 'Forward', GREEN) + fwdbutton(wri, 30, 130, EmptyScreen, 'Forward', GREEN) CloseButton(wri) def after_open(self): # After graph has been drawn @@ -130,7 +130,7 @@ class PolarClip(Screen): super().__init__() self.g = PolarGraph(wri, 2, 2, fgcolor=GREEN, gridcolor=LIGHTGREEN) Label(wri, 100, 2, 'Clipping of polar data.') - fwdbutton(wri, 2, 130, EmptyScreen, 'Forward', GREEN) + fwdbutton(wri, 30, 130, EmptyScreen, 'Forward', GREEN) CloseButton(wri) def after_open(self): # After graph has been drawn @@ -148,7 +148,7 @@ class RTPolar(Screen): super().__init__() self.g = PolarGraph(wri, 2, 2, fgcolor=GREEN, gridcolor=LIGHTGREEN) Label(wri, 100, 2, 'Realtime polar data.') - fwdbutton(wri, 2, 130, EmptyScreen, 'Forward', GREEN) + fwdbutton(wri, 30, 130, EmptyScreen, 'Forward', GREEN) CloseButton(wri) def after_open(self): # After graph has been drawn @@ -168,7 +168,7 @@ class RTRect(Screen): super().__init__() self.g = CartesianGraph(wri, 2, 2, fgcolor=GREEN, gridcolor=LIGHTGREEN) Label(wri, 100, 2, 'Realtime discontinuous data.') - fwdbutton(wri, 2, 130, EmptyScreen, 'Forward', GREEN) + fwdbutton(wri, 30, 130, EmptyScreen, 'Forward', GREEN) CloseButton(wri) def after_open(self): # After graph has been drawn @@ -198,7 +198,7 @@ class TSeq(Screen): self.g = CartesianGraph(wri, 2, 2, xorigin = 10, fgcolor=GREEN, gridcolor=LIGHTGREEN, bdcolor=False) Label(wri, 100, 2, 'Time sequence.') - fwdbutton(wri, 2, 130, EmptyScreen, 'Forward', GREEN) + fwdbutton(wri, 30, 130, EmptyScreen, 'Forward', GREEN) CloseButton(wri) def after_open(self): # After graph has been drawn @@ -241,8 +241,8 @@ class BaseScreen(Screen): Screen.change(d[lb.textvalue()]) def test(): - if display.height < 240 or display.width < 320: - print(' This test requires a display of at least 320x240 pixels.') + if display.height < 128 or display.width < 200: + print(' This test requires a display of at least 128x200 pixels.') else: print('Testing micro-gui...') Screen.change(BaseScreen) diff --git a/gui/demos/various.py b/gui/demos/various.py index 82637c7..b21cdef 100644 --- a/gui/demos/various.py +++ b/gui/demos/various.py @@ -53,15 +53,18 @@ class FooScreen(Screen): self.rb0 = None self.bs0 = None wri = CWriter(ssd, arial10, GREEN, BLACK, verbose=False) - lbltim = Label(wri, 50, 85, 'this is a test', bdcolor=RED) + lbltim = Label(wri, 65, 100, 'this is a test', bdcolor=RED) - m0 = Meter(wri, 20, 240, divisions = 4, ptcolor=YELLOW, - label='left', style=Meter.LINE, legends=('0.0', '0.5', '1.0')) + m0 = Meter(wri, 10, 240, divisions = 4, ptcolor=YELLOW, height=80, width=15, + label='Meter example', style=Meter.BAR, legends=('0.0', '0.5', '1.0')) # Instantiate displayable objects. bgcolor forces complete redraw. dial = Dial(wri, 2, 2, height = 75, ticks = 12, bgcolor=BLACK, bdcolor=None, label=120) # Border in fg color scale = Scale(wri, 2, 100, width = 124, tickcb = tickcb, pointercolor=RED, fontcolor=YELLOW, bdcolor=CYAN) + row = 105 + col = 2 + Label(wri, row, col, 'Normal buttons') # Four Button instances row = 120 ht = 30 @@ -73,8 +76,9 @@ class FooScreen(Screen): self.bs = ButtonList(self.callback) self.bs0 = None col+= 50 + Label(wri, row - 15, col, 'ButtonList') for t in table_buttonset: # Buttons overlay each other at same location - button = self.bs.add_button(wri, 120, col, shape=RECTANGLE, textcolor=BLUE, height=30, **t) + button = self.bs.add_button(wri, row, col, shape=RECTANGLE, textcolor=BLUE, height=30, **t) if self.bs0 is None: # Save for reset button callback self.bs0 = button @@ -82,21 +86,25 @@ class FooScreen(Screen): col+= 60 btn = Button(wri, row, col, height=30, callback=self.rstcb, text='reset', litcolor=RED, fgcolor=GREEN, bgcolor=DARKGREEN) + col = 2 + row = 170 + Label(wri, row, col, 'Radio buttons') # Radio buttons - col= 2 + row = 185 self.rb = RadioButtons(BLUE, self.rbcb) # color of selected button self.rb0 = None for t in table_radiobuttons: - button = self.rb.add_button(wri, 160, col, textcolor = WHITE, + button = self.rb.add_button(wri, row, col, textcolor = WHITE, fgcolor = BLUE, bgcolor = DARKBLUE, shape=CIRCLE, height = 30, **t) if self.rb0 is None: # Save for reset button callback self.rb0 = button col+= 35 # Checkbox col+= 35 - Checkbox(wri, 160, col, callback=self.cbcb) + Label(wri, row - 15, col, 'Checkbox and LED') + Checkbox(wri, row, col, callback=self.cbcb) col+= 40 - self.led = LED(wri, 160, col, color=YELLOW, bdcolor=GREEN) + self.led = LED(wri, row, col, color=YELLOW, bdcolor=GREEN) CloseButton(wri) asyncio.create_task(run(dial, lbltim, m0, scale)) @@ -109,7 +117,7 @@ class FooScreen(Screen): def rstcb(self, button): print('Reset button: init ButtonList and RadioButtons') - self.bs.value(self.bs0) # BUG This is calling ButtonList.value which calls ._update -> Screen.select which changes currency + self.bs.value(self.bs0) self.rb.value(self.rb0) def cbcb(self, cb): diff --git a/gui/widgets/meter.py b/gui/widgets/meter.py index 47039ba..b97860a 100644 --- a/gui/widgets/meter.py +++ b/gui/widgets/meter.py @@ -1,7 +1,7 @@ # meter.py Extension to ugui providing a linear "meter" widget. # Released under the MIT License (MIT). See LICENSE. -# Copyright (c) 2019 Peter Hinch +# Copyright (c) 2021 Peter Hinch from gui.core.ugui import Widget, display from gui.widgets.label import Label diff --git a/gui/widgets/scale.py b/gui/widgets/scale.py index ecca0dc..d52a414 100644 --- a/gui/widgets/scale.py +++ b/gui/widgets/scale.py @@ -9,7 +9,7 @@ from gui.core.ugui import LinearIO, display from hardware_setup import ssd # Display driver for Writer from gui.core.writer import Writer -from gui.core.colors import BLACK +from gui.core.colors import * dolittle = lambda *_ : None @@ -84,6 +84,7 @@ class Scale(LinearIO): # So pixels per unit value == win_width/200 win_width: int = x1 - x0 ticks: int = self.ticks # Total # of ticks visible and hidden + txtcolor = GREY if self.greyed_out() else self.fontcolor while True: x: int = x0 + (fx * win_width) // 200 # Current X position ys: int # Start Y position for tick @@ -94,7 +95,7 @@ class Scale(LinearIO): txt = self.legendcb(self._fvalue(iv * 10)) tlen = wri.stringlen(txt) Writer.set_textpos(ssd, y0, min(x, x1 - tlen)) - wri.setcolor(self.fontcolor, self.bgcolor) + wri.setcolor(txtcolor, self.bgcolor) wri.printstring(txt) wri.setcolor() ys = self.ldy0 # Large tick diff --git a/gui/widgets/scale_log.py b/gui/widgets/scale_log.py index d9cefc2..718d9ec 100644 --- a/gui/widgets/scale_log.py +++ b/gui/widgets/scale_log.py @@ -15,7 +15,7 @@ from math import log10 from gui.core.ugui import LinearIO, display from hardware_setup import ssd # Display driver for Writer from gui.core.writer import Writer -from gui.core.colors import BLACK +from gui.core.colors import * # Null function dolittle = lambda *_ : None @@ -86,6 +86,7 @@ class ScaleLog(LinearIO): vc = self._value # Current value, corresponds to centre of display d = int(log10(vc)) - 1 # 10**d is start of a decade guaranteed to be outside display vs = max(10 ** d, 1.0) # vs: start value of current decade + txtcolor = GREY if self.greyed_out() else self.fontcolor while True: # For each decade until we run out of space done = True # Assume completion xs: float = xc - dw * log10(vc / vs) # x location of start of scale @@ -102,7 +103,7 @@ class ScaleLog(LinearIO): txt = self.legendcb(vt) tlen = wri.stringlen(txt) Writer.set_textpos(ssd, y0, min(x, x1 - tlen)) - wri.setcolor(self.fontcolor, self.bgcolor) + wri.setcolor(txtcolor, self.bgcolor) wri.printstring(txt) ys = self.ldy0 # Large tick yl = self.ldl