commit 247a92c51940932cc7a85d6d7c3900bdd80f8e1f Author: alex Date: Thu Mar 5 22:40:23 2026 +0100 initial commit diff --git a/colors.py b/colors.py new file mode 100644 index 0000000..3aa3b10 --- /dev/null +++ b/colors.py @@ -0,0 +1,19 @@ + + +def TFTColor(aR, aG, aB): + '''Create a 16 bit rgb value from the given R,G,B from 0-255. + This assumes rgb 565 layout and will be incorrect for bgr.''' + return ((aR & 0xF8) << 8) | ((aG & 0xFC) << 3) | (aB >> 3) + +BLACK = 0 +RED = TFTColor(0xFF, 0x00, 0x00) +MAROON = TFTColor(0x80, 0x00, 0x00) +GREEN = TFTColor(0x00, 0xFF, 0x00) +FOREST = TFTColor(0x00, 0x80, 0x80) +BLUE = TFTColor(0x00, 0x00, 0xFF) +NAVY = TFTColor(0x00, 0x00, 0x80) +CYAN = TFTColor(0x00, 0xFF, 0xFF) +YELLOW = TFTColor(0xFF, 0xFF, 0x00) +PURPLE = TFTColor(0xFF, 0x00, 0xFF) +WHITE = TFTColor(0xFF, 0xFF, 0xFF) +GRAY = TFTColor(0x80, 0x80, 0x80) \ No newline at end of file diff --git a/display.py b/display.py new file mode 100644 index 0000000..dd4fc2a --- /dev/null +++ b/display.py @@ -0,0 +1,5 @@ + + +class Display: + def __init__(self): + pass diff --git a/main.py b/main.py new file mode 100644 index 0000000..a391e0a --- /dev/null +++ b/main.py @@ -0,0 +1,43 @@ +from machine import Pin, SPI +from st7735 import TFT +from colors import * +from page import Page +from widgets import * + + +spi = SPI(1, mosi=Pin(6), sck=Pin(4), baudrate=20000000, polarity=0, phase=0) +disp = TFT(spi, 0, 2, 1) + +disp.initr() +disp.rgb(True) +disp.rotation(2) + +page = Page(disp, b_color=WHITE) +lbl = Label("My Label", (10, 10), back_color=GREEN) +page.widgets.append(lbl) + +chbox = Checkbox("My Checkbox", (10,30), f_color=MAROON, back_color=GREEN) +page.widgets.append(chbox) +chbox1 = Checkbox("My Checkbox", (10,50), checked=True, f_color=MAROON, back_color=GREEN) +page.widgets.append(chbox1) + +rdbut = RadioButton("My RadioButton", (10,70), f_color=MAROON, back_color=GREEN) +page.widgets.append(rdbut) +rdbut1 = RadioButton("My RadioButton1", (10,90), checked=True, f_color=MAROON, back_color=GREEN) +page.widgets.append(rdbut1) + +lbl1 = Label("Focus", (10, 110), back_color=GREEN) +lbl1.set_focus(True) +page.widgets.append(lbl1) + +page.draw() + + +import time +time.sleep(2) +page2 = Page(disp, visible = False, b_color=0) +page2.widgets.append(Label("Hello 2. Screen", (10, 10),f_color=MAROON, back_color=WHITE)) +lbl1.set_focus(False) +page.set_visible(False) +page2.set_visible(True) +page2.draw() \ No newline at end of file diff --git a/page.py b/page.py new file mode 100644 index 0000000..c4abdb0 --- /dev/null +++ b/page.py @@ -0,0 +1,21 @@ +from colors import * + +class Page: + def __init__(self, display, visible = True, b_color=WHITE): + self.visible = visible + self.widgets = list() + self.display = display + self.b_color = b_color + + def draw(self): + if self.visible: + self.display.fill(self.b_color) + for w in self.widgets: + w.draw(self.display) + + def set_visible(self, visible): + self.visible = visible + + + def get_visible(self): + return self.visible \ No newline at end of file diff --git a/ssd1306.py b/ssd1306.py new file mode 100644 index 0000000..e0fe7ec --- /dev/null +++ b/ssd1306.py @@ -0,0 +1,155 @@ +# MicroPython SSD1306 OLED driver, I2C and SPI interfaces + +from micropython import const +import framebuf + + +# register definitions +SET_CONTRAST = const(0x81) +SET_ENTIRE_ON = const(0xA4) +SET_NORM_INV = const(0xA6) +SET_DISP = const(0xAE) +SET_MEM_ADDR = const(0x20) +SET_COL_ADDR = const(0x21) +SET_PAGE_ADDR = const(0x22) +SET_DISP_START_LINE = const(0x40) +SET_SEG_REMAP = const(0xA0) +SET_MUX_RATIO = const(0xA8) +SET_COM_OUT_DIR = const(0xC0) +SET_DISP_OFFSET = const(0xD3) +SET_COM_PIN_CFG = const(0xDA) +SET_DISP_CLK_DIV = const(0xD5) +SET_PRECHARGE = const(0xD9) +SET_VCOM_DESEL = const(0xDB) +SET_CHARGE_PUMP = const(0x8D) + +# Subclassing FrameBuffer provides support for graphics primitives +# http://docs.micropython.org/en/latest/pyboard/library/framebuf.html +class SSD1306(framebuf.FrameBuffer): + def __init__(self, width, height, external_vcc): + self.width = width + self.height = height + self.external_vcc = external_vcc + self.pages = self.height // 8 + self.buffer = bytearray(self.pages * self.width) + super().__init__(self.buffer, self.width, self.height, framebuf.MONO_VLSB) + self.init_display() + + def init_display(self): + for cmd in ( + SET_DISP | 0x00, # off + # address setting + SET_MEM_ADDR, + 0x00, # horizontal + # resolution and layout + SET_DISP_START_LINE | 0x00, + SET_SEG_REMAP | 0x01, # column addr 127 mapped to SEG0 + SET_MUX_RATIO, + self.height - 1, + SET_COM_OUT_DIR | 0x08, # scan from COM[N] to COM0 + SET_DISP_OFFSET, + 0x00, + SET_COM_PIN_CFG, + 0x02 if self.width > 2 * self.height else 0x12, + # timing and driving scheme + SET_DISP_CLK_DIV, + 0x80, + SET_PRECHARGE, + 0x22 if self.external_vcc else 0xF1, + SET_VCOM_DESEL, + 0x30, # 0.83*Vcc + # display + SET_CONTRAST, + 0xFF, # maximum + SET_ENTIRE_ON, # output follows RAM contents + SET_NORM_INV, # not inverted + # charge pump + SET_CHARGE_PUMP, + 0x10 if self.external_vcc else 0x14, + SET_DISP | 0x01, + ): # on + self.write_cmd(cmd) + self.fill(0) + self.show() + + def poweroff(self): + self.write_cmd(SET_DISP | 0x00) + + def poweron(self): + self.write_cmd(SET_DISP | 0x01) + + def contrast(self, contrast): + self.write_cmd(SET_CONTRAST) + self.write_cmd(contrast) + + def invert(self, invert): + self.write_cmd(SET_NORM_INV | (invert & 1)) + + def show(self): + x0 = 0 + x1 = self.width - 1 + if self.width == 64: + # displays with width of 64 pixels are shifted by 32 + x0 += 32 + x1 += 32 + self.write_cmd(SET_COL_ADDR) + self.write_cmd(x0) + self.write_cmd(x1) + self.write_cmd(SET_PAGE_ADDR) + self.write_cmd(0) + self.write_cmd(self.pages - 1) + self.write_data(self.buffer) + + +class SSD1306_I2C(SSD1306): + def __init__(self, width, height, i2c, addr=0x3C, external_vcc=False): + self.i2c = i2c + self.addr = addr + self.temp = bytearray(2) + self.write_list = [b"\x40", None] # Co=0, D/C#=1 + super().__init__(width, height, external_vcc) + + def write_cmd(self, cmd): + self.temp[0] = 0x80 # Co=1, D/C#=0 + self.temp[1] = cmd + self.i2c.writeto(self.addr, self.temp) + + def write_data(self, buf): + self.write_list[1] = buf + self.i2c.writevto(self.addr, self.write_list) + + +class SSD1306_SPI(SSD1306): + def __init__(self, width, height, spi, dc, res, cs, external_vcc=False): + self.rate = 10 * 1024 * 1024 + dc.init(dc.OUT, value=0) + res.init(res.OUT, value=0) + cs.init(cs.OUT, value=1) + self.spi = spi + self.dc = dc + self.res = res + self.cs = cs + import time + + self.res(1) + time.sleep_ms(1) + self.res(0) + time.sleep_ms(10) + self.res(1) + super().__init__(width, height, external_vcc) + + def write_cmd(self, cmd): + self.spi.init(baudrate=self.rate, polarity=0, phase=0) + self.cs(1) + self.dc(0) + self.cs(0) + self.spi.write(bytearray([cmd])) + self.cs(1) + + def write_data(self, buf): + self.spi.init(baudrate=self.rate, polarity=0, phase=0) + self.cs(1) + self.dc(1) + self.cs(0) + self.spi.write(buf) + self.cs(1) \ No newline at end of file diff --git a/st7735.py b/st7735.py new file mode 100644 index 0000000..fbbf865 --- /dev/null +++ b/st7735.py @@ -0,0 +1,922 @@ +# driver for Sainsmart 1.8" TFT display ST7735 +# Translated by Guy Carver from the ST7735 sample code. +# Modirfied for micropython-esp32 by boochow + +import machine +import time +from math import sqrt +from colors import * + +# TFTRotations and TFTRGB are bits to set +# on MADCTL to control display rotation/color layout +# Looking at display with pins on top. +# 00 = upper left printing right +# 10 = does nothing (MADCTL_ML) +# 20 = upper left printing down (backwards) (Vertical flip) +# 40 = upper right printing left (backwards) (X Flip) +# 80 = lower left printing right (backwards) (Y Flip) +# 04 = (MADCTL_MH) + +# 60 = 90 right rotation +# C0 = 180 right rotation +# A0 = 270 right rotation +TFTRotations = [0x00, 0x60, 0xC0, 0xA0] +TFTBGR = 0x08 # When set color is bgr else rgb. +TFTRGB = 0x00 + + +# @micropython.native +def clamp(aValue, aMin, aMax): + return max(aMin, min(aMax, aValue)) + + +# @micropython.native +def TFTColor(aR, aG, aB): + '''Create a 16 bit rgb value from the given R,G,B from 0-255. + This assumes rgb 565 layout and will be incorrect for bgr.''' + return ((aR & 0xF8) << 8) | ((aG & 0xFC) << 3) | (aB >> 3) + + +ScreenSize = (128, 160) + + +class TFT(object): + """Sainsmart TFT 7735 display driver.""" + + NOP = 0x0 + SWRESET = 0x01 + RDDID = 0x04 + RDDST = 0x09 + + SLPIN = 0x10 + SLPOUT = 0x11 + PTLON = 0x12 + NORON = 0x13 + + INVOFF = 0x20 + INVON = 0x21 + DISPOFF = 0x28 + DISPON = 0x29 + CASET = 0x2A + RASET = 0x2B + RAMWR = 0x2C + RAMRD = 0x2E + + VSCRDEF = 0x33 + VSCSAD = 0x37 + + COLMOD = 0x3A + MADCTL = 0x36 + + FRMCTR1 = 0xB1 + FRMCTR2 = 0xB2 + FRMCTR3 = 0xB3 + INVCTR = 0xB4 + DISSET5 = 0xB6 + + PWCTR1 = 0xC0 + PWCTR2 = 0xC1 + PWCTR3 = 0xC2 + PWCTR4 = 0xC3 + PWCTR5 = 0xC4 + VMCTR1 = 0xC5 + + RDID1 = 0xDA + RDID2 = 0xDB + RDID3 = 0xDC + RDID4 = 0xDD + + PWCTR6 = 0xFC + + GMCTRP1 = 0xE0 + GMCTRN1 = 0xE1 + + + + + @staticmethod + def color(aR, aG, aB): + '''Create a 565 rgb TFTColor value''' + return TFTColor(aR, aG, aB) + + def __init__(self, spi, aDC, aReset, aCS): + """aLoc SPI pin location is either 1 for 'X' or 2 for 'Y'. + aDC is the DC pin and aReset is the reset pin.""" + self._size = ScreenSize + self._offset = bytearray([0, 0]) + self.rotate = 0 # Vertical with top toward pins. + self._rgb = True # color order of rgb. + self.tfa = 0 # top fixed area + self.bfa = 0 # bottom fixed area + self.dc = machine.Pin(aDC, machine.Pin.OUT, machine.Pin.PULL_DOWN) + self.reset = machine.Pin(aReset, machine.Pin.OUT, machine.Pin.PULL_DOWN) + self.cs = machine.Pin(aCS, machine.Pin.OUT, machine.Pin.PULL_DOWN) + self.cs(1) + self.spi = spi + self.colorData = bytearray(2) + self.windowLocData = bytearray(4) + + def size(self): + return self._size + + # @micropython.native + def on(self, aTF=True): + '''Turn display on or off.''' + self._writecommand(TFT.DISPON if aTF else TFT.DISPOFF) + + # @micropython.native + def invertcolor(self, aBool): + '''Invert the color data IE: Black = White.''' + self._writecommand(TFT.INVON if aBool else TFT.INVOFF) + + # @micropython.native + def rgb(self, aTF=True): + '''True = rgb else bgr''' + self._rgb = aTF + self._setMADCTL() + + # @micropython.native + def rotation(self, aRot): + '''0 - 3. Starts vertical with top toward pins and rotates 90 deg + clockwise each step.''' + if (0 <= aRot < 4): + rotchange = self.rotate ^ aRot + self.rotate = aRot + # If switching from vertical to horizontal swap x,y + # (indicated by bit 0 changing). + if (rotchange & 1): + self._size = (self._size[1], self._size[0]) + self._setMADCTL() + + # @micropython.native + def pixel(self, aPos, aColor): + '''Draw a pixel at the given position''' + if 0 <= aPos[0] < self._size[0] and 0 <= aPos[1] < self._size[1]: + self._setwindowpoint(aPos) + self._pushcolor(aColor) + + # @micropython.native + def text(self, aPos, aString, aColor, aFont, aSize=1, nowrap=False): + '''Draw a text at the given position. If the string reaches the end of the + display it is wrapped to aPos[0] on the next line. aSize may be an integer + which will size the font uniformly on w,h or a or any type that may be + indexed with [0] or [1].''' + + if aFont == None: + return + + # Make a size either from single value or 2 elements. + if (type(aSize) == int) or (type(aSize) == float): + wh = (aSize, aSize) + else: + wh = aSize + + px, py = aPos + width = wh[0] * aFont["Width"] + 1 + for c in aString: + self.char((px, py), c, aColor, aFont, wh) + px += width + # We check > rather than >= to let the right (blank) edge of the + # character print off the right of the screen. + if px + width > self._size[0]: + if nowrap: + break + else: + py += aFont["Height"] * wh[1] + 1 + px = aPos[0] + + # @micropython.native + def char(self, aPos, aChar, aColor, aFont, aSizes): + '''Draw a character at the given position using the given font and color. + aSizes is a tuple with x, y as integer scales indicating the + # of pixels to draw for each pixel in the character.''' + + if aFont == None: + return + + startchar = aFont['Start'] + endchar = aFont['End'] + + ci = ord(aChar) + if (startchar <= ci <= endchar): + fontw = aFont['Width'] + fonth = aFont['Height'] + ci = (ci - startchar) * fontw + + charA = aFont["Data"][ci:ci + fontw] + px = aPos[0] + if aSizes[0] <= 1 and aSizes[1] <= 1: + buf = bytearray(2 * fonth * fontw) + for q in range(fontw): + c = charA[q] + for r in range(fonth): + if c & 0x01: + pos = 2 * (r * fontw + q) + buf[pos] = aColor >> 8 + buf[pos + 1] = aColor & 0xff + c >>= 1 + self.image(aPos[0], aPos[1], aPos[0] + fontw - 1, aPos[1] + fonth - 1, buf) + else: + for c in charA: + py = aPos[1] + for r in range(fonth): + if c & 0x01: + self.fillrect((px, py), aSizes, aColor) + py += aSizes[1] + c >>= 1 + px += aSizes[0] + + # @micropython.native + def line(self, aStart, aEnd, aColor): + '''Draws a line from aStart to aEnd in the given color. Vertical or horizontal + lines are forwarded to vline and hline.''' + if aStart[0] == aEnd[0]: + # Make sure we use the smallest y. + pnt = aEnd if (aEnd[1] < aStart[1]) else aStart + self.vline(pnt, abs(aEnd[1] - aStart[1]) + 1, aColor) + elif aStart[1] == aEnd[1]: + # Make sure we use the smallest x. + pnt = aEnd if aEnd[0] < aStart[0] else aStart + self.hline(pnt, abs(aEnd[0] - aStart[0]) + 1, aColor) + else: + px, py = aStart + ex, ey = aEnd + dx = ex - px + dy = ey - py + inx = 1 if dx > 0 else -1 + iny = 1 if dy > 0 else -1 + + dx = abs(dx) + dy = abs(dy) + if (dx >= dy): + dy <<= 1 + e = dy - dx + dx <<= 1 + while (px != ex): + self.pixel((px, py), aColor) + if (e >= 0): + py += iny + e -= dx + e += dy + px += inx + else: + dx <<= 1 + e = dx - dy + dy <<= 1 + while (py != ey): + self.pixel((px, py), aColor) + if (e >= 0): + px += inx + e -= dy + e += dx + py += iny + + # @micropython.native + def vline(self, aStart, aLen, aColor): + '''Draw a vertical line from aStart for aLen. aLen may be negative.''' + start = (clamp(aStart[0], 0, self._size[0]), clamp(aStart[1], 0, self._size[1])) + stop = (start[0], clamp(start[1] + aLen, 0, self._size[1])) + # Make sure smallest y 1st. + if (stop[1] < start[1]): + start, stop = stop, start + self._setwindowloc(start, stop) + self._setColor(aColor) + self._draw(aLen) + + # @micropython.native + def hline(self, aStart, aLen, aColor): + '''Draw a horizontal line from aStart for aLen. aLen may be negative.''' + start = (clamp(aStart[0], 0, self._size[0]), clamp(aStart[1], 0, self._size[1])) + stop = (clamp(start[0] + aLen, 0, self._size[0]), start[1]) + # Make sure smallest x 1st. + if (stop[0] < start[0]): + start, stop = stop, start + self._setwindowloc(start, stop) + self._setColor(aColor) + self._draw(aLen) + + # @micropython.native + def rect(self, aStart, aSize, aColor): + '''Draw a hollow rectangle. aStart is the smallest coordinate corner + and aSize is a tuple indicating width, height.''' + self.hline(aStart, aSize[0], aColor) + self.hline((aStart[0], aStart[1] + aSize[1] - 1), aSize[0], aColor) + self.vline(aStart, aSize[1], aColor) + self.vline((aStart[0] + aSize[0] - 1, aStart[1]), aSize[1], aColor) + + # @micropython.native + def fillrect(self, aStart, aSize, aColor): + '''Draw a filled rectangle. aStart is the smallest coordinate corner + and aSize is a tuple indicating width, height.''' + start = (clamp(aStart[0], 0, self._size[0]), clamp(aStart[1], 0, self._size[1])) + end = (clamp(start[0] + aSize[0] - 1, 0, self._size[0]), clamp(start[1] + aSize[1] - 1, 0, self._size[1])) + + if (end[0] < start[0]): + tmp = end[0] + end = (start[0], end[1]) + start = (tmp, start[1]) + if (end[1] < start[1]): + tmp = end[1] + end = (end[0], start[1]) + start = (start[0], tmp) + + self._setwindowloc(start, end) + numPixels = (end[0] - start[0] + 1) * (end[1] - start[1] + 1) + self._setColor(aColor) + self._draw(numPixels) + + # @micropython.native + def circle(self, aPos, aRadius, aColor): + '''Draw a hollow circle with the given radius and color with aPos as center.''' + self.colorData[0] = aColor >> 8 + self.colorData[1] = aColor + xend = int(0.7071 * aRadius) + 1 + rsq = aRadius * aRadius + for x in range(xend): + y = int(sqrt(rsq - x * x)) + xp = aPos[0] + x + yp = aPos[1] + y + xn = aPos[0] - x + yn = aPos[1] - y + xyp = aPos[0] + y + yxp = aPos[1] + x + xyn = aPos[0] - y + yxn = aPos[1] - x + + self._setwindowpoint((xp, yp)) + self._writedata(self.colorData) + self._setwindowpoint((xp, yn)) + self._writedata(self.colorData) + self._setwindowpoint((xn, yp)) + self._writedata(self.colorData) + self._setwindowpoint((xn, yn)) + self._writedata(self.colorData) + self._setwindowpoint((xyp, yxp)) + self._writedata(self.colorData) + self._setwindowpoint((xyp, yxn)) + self._writedata(self.colorData) + self._setwindowpoint((xyn, yxp)) + self._writedata(self.colorData) + self._setwindowpoint((xyn, yxn)) + self._writedata(self.colorData) + + # @micropython.native + def fillcircle(self, aPos, aRadius, aColor): + '''Draw a filled circle with given radius and color with aPos as center''' + rsq = aRadius * aRadius + for x in range(aRadius): + y = int(sqrt(rsq - x * x)) + y0 = aPos[1] - y + ey = y0 + y * 2 + y0 = clamp(y0, 0, self._size[1]) + ln = abs(ey - y0) + 1; + + self.vline((aPos[0] + x, y0), ln, aColor) + self.vline((aPos[0] - x, y0), ln, aColor) + + def fill(self, aColor=BLACK): + '''Fill screen with the given color.''' + self.fillrect((0, 0), self._size, aColor) + + def image(self, x0, y0, x1, y1, data): + self._setwindowloc((x0, y0), (x1, y1)) + self._writedata(data) + + def setvscroll(self, tfa, bfa): + ''' set vertical scroll area ''' + self._writecommand(TFT.VSCRDEF) + data2 = bytearray([0, tfa]) + self._writedata(data2) + data2[1] = 162 - tfa - bfa + self._writedata(data2) + data2[1] = bfa + self._writedata(data2) + self.tfa = tfa + self.bfa = bfa + + def vscroll(self, value): + a = value + self.tfa + if (a + self.bfa > 162): + a = 162 - self.bfa + self._vscrolladdr(a) + + def _vscrolladdr(self, addr): + self._writecommand(TFT.VSCSAD) + data2 = bytearray([addr >> 8, addr & 0xff]) + self._writedata(data2) + + # @micropython.native + def _setColor(self, aColor): + self.colorData[0] = aColor >> 8 + self.colorData[1] = aColor + self.buf = bytes(self.colorData) * 32 + + # @micropython.native + def _draw(self, aPixels): + '''Send given color to the device aPixels times.''' + + self.dc(1) + self.cs(0) + for i in range(aPixels // 32): + self.spi.write(self.buf) + rest = (int(aPixels) % 32) + if rest > 0: + buf2 = bytes(self.colorData) * rest + self.spi.write(buf2) + self.cs(1) + + # @micropython.native + def _setwindowpoint(self, aPos): + '''Set a single point for drawing a color to.''' + x = self._offset[0] + int(aPos[0]) + y = self._offset[1] + int(aPos[1]) + self._writecommand(TFT.CASET) # Column address set. + self.windowLocData[0] = self._offset[0] + self.windowLocData[1] = x + self.windowLocData[2] = self._offset[0] + self.windowLocData[3] = x + self._writedata(self.windowLocData) + + self._writecommand(TFT.RASET) # Row address set. + self.windowLocData[0] = self._offset[1] + self.windowLocData[1] = y + self.windowLocData[2] = self._offset[1] + self.windowLocData[3] = y + self._writedata(self.windowLocData) + self._writecommand(TFT.RAMWR) # Write to RAM. + + # @micropython.native + def _setwindowloc(self, aPos0, aPos1): + '''Set a rectangular area for drawing a color to.''' + self._writecommand(TFT.CASET) # Column address set. + self.windowLocData[0] = self._offset[0] + self.windowLocData[1] = self._offset[0] + int(aPos0[0]) + self.windowLocData[2] = self._offset[0] + self.windowLocData[3] = self._offset[0] + int(aPos1[0]) + self._writedata(self.windowLocData) + + self._writecommand(TFT.RASET) # Row address set. + self.windowLocData[0] = self._offset[1] + self.windowLocData[1] = self._offset[1] + int(aPos0[1]) + self.windowLocData[2] = self._offset[1] + self.windowLocData[3] = self._offset[1] + int(aPos1[1]) + self._writedata(self.windowLocData) + + self._writecommand(TFT.RAMWR) # Write to RAM. + + # @micropython.native + def _writecommand(self, aCommand): + '''Write given command to the device.''' + self.dc(0) + self.cs(0) + self.spi.write(bytearray([aCommand])) + self.cs(1) + + # @micropython.native + def _writedata(self, aData): + '''Write given data to the device. This may be + either a single int or a bytearray of values.''' + self.dc(1) + self.cs(0) + self.spi.write(aData) + self.cs(1) + + # @micropython.native + def _pushcolor(self, aColor): + '''Push given color to the device.''' + self.colorData[0] = aColor >> 8 + self.colorData[1] = aColor + self._writedata(self.colorData) + + # @micropython.native + def _setMADCTL(self): + '''Set screen rotation and RGB/BGR format.''' + self._writecommand(TFT.MADCTL) + rgb = TFTRGB if self._rgb else TFTBGR + self._writedata(bytearray([TFTRotations[self.rotate] | rgb])) + + # @micropython.native + def _reset(self): + '''Reset the device.''' + self.dc(0) + self.reset(1) + time.sleep_us(500) + self.reset(0) + time.sleep_us(500) + self.reset(1) + time.sleep_us(500) + + def initb(self): + '''Initialize blue tab version.''' + self._size = (ScreenSize[0] + 2, ScreenSize[1] + 1) + self._reset() + self._writecommand(TFT.SWRESET) # Software reset. + time.sleep_us(50) + self._writecommand(TFT.SLPOUT) # out of sleep mode. + time.sleep_us(500) + + data1 = bytearray(1) + self._writecommand(TFT.COLMOD) # Set color mode. + data1[0] = 0x05 # 16 bit color. + self._writedata(data1) + time.sleep_us(10) + + data3 = bytearray([0x00, 0x06, 0x03]) # fastest refresh, 6 lines front, 3 lines back. + self._writecommand(TFT.FRMCTR1) # Frame rate control. + self._writedata(data3) + time.sleep_us(10) + + self._writecommand(TFT.MADCTL) + data1[0] = 0x08 # row address/col address, bottom to top refresh + self._writedata(data1) + + data2 = bytearray(2) + self._writecommand(TFT.DISSET5) # Display settings + data2[0] = 0x15 # 1 clock cycle nonoverlap, 2 cycle gate rise, 3 cycle oscil, equalize + data2[1] = 0x02 # fix on VTL + self._writedata(data2) + + self._writecommand(TFT.INVCTR) # Display inversion control + data1[0] = 0x00 # Line inversion. + self._writedata(data1) + + self._writecommand(TFT.PWCTR1) # Power control + data2[0] = 0x02 # GVDD = 4.7V + data2[1] = 0x70 # 1.0uA + self._writedata(data2) + time.sleep_us(10) + + self._writecommand(TFT.PWCTR2) # Power control + data1[0] = 0x05 # VGH = 14.7V, VGL = -7.35V + self._writedata(data1) + + self._writecommand(TFT.PWCTR3) # Power control + data2[0] = 0x01 # Opamp current small + data2[1] = 0x02 # Boost frequency + self._writedata(data2) + + self._writecommand(TFT.VMCTR1) # Power control + data2[0] = 0x3C # VCOMH = 4V + data2[1] = 0x38 # VCOML = -1.1V + self._writedata(data2) + time.sleep_us(10) + + self._writecommand(TFT.PWCTR6) # Power control + data2[0] = 0x11 + data2[1] = 0x15 + self._writedata(data2) + + # These different values don't seem to make a difference. + # dataGMCTRP = bytearray([0x0f, 0x1a, 0x0f, 0x18, 0x2f, 0x28, 0x20, 0x22, 0x1f, + # 0x1b, 0x23, 0x37, 0x00, 0x07, 0x02, 0x10]) + dataGMCTRP = bytearray([0x02, 0x1c, 0x07, 0x12, 0x37, 0x32, 0x29, 0x2d, 0x29, + 0x25, 0x2b, 0x39, 0x00, 0x01, 0x03, 0x10]) + self._writecommand(TFT.GMCTRP1) + self._writedata(dataGMCTRP) + + # dataGMCTRN = bytearray([0x0f, 0x1b, 0x0f, 0x17, 0x33, 0x2c, 0x29, 0x2e, 0x30, + # 0x30, 0x39, 0x3f, 0x00, 0x07, 0x03, 0x10]) + dataGMCTRN = bytearray([0x03, 0x1d, 0x07, 0x06, 0x2e, 0x2c, 0x29, 0x2d, 0x2e, + 0x2e, 0x37, 0x3f, 0x00, 0x00, 0x02, 0x10]) + self._writecommand(TFT.GMCTRN1) + self._writedata(dataGMCTRN) + time.sleep_us(10) + + self._writecommand(TFT.CASET) # Column address set. + self.windowLocData[0] = 0x00 + self.windowLocData[1] = 2 # Start at column 2 + self.windowLocData[2] = 0x00 + self.windowLocData[3] = self._size[0] - 1 + self._writedata(self.windowLocData) + + self._writecommand(TFT.RASET) # Row address set. + self.windowLocData[1] = 1 # Start at row 2. + self.windowLocData[3] = self._size[1] - 1 + self._writedata(self.windowLocData) + + self._writecommand(TFT.NORON) # Normal display on. + time.sleep_us(10) + + self._writecommand(TFT.RAMWR) + time.sleep_us(500) + + self._writecommand(TFT.DISPON) + self.cs(1) + time.sleep_us(500) + + def initr(self): + '''Initialize a red tab version.''' + self._reset() + + self._writecommand(TFT.SWRESET) # Software reset. + time.sleep_us(150) + self._writecommand(TFT.SLPOUT) # out of sleep mode. + time.sleep_us(500) + + data3 = bytearray([0x01, 0x2C, 0x2D]) # fastest refresh, 6 lines front, 3 lines back. + self._writecommand(TFT.FRMCTR1) # Frame rate control. + self._writedata(data3) + + self._writecommand(TFT.FRMCTR2) # Frame rate control. + self._writedata(data3) + + data6 = bytearray([0x01, 0x2c, 0x2d, 0x01, 0x2c, 0x2d]) + self._writecommand(TFT.FRMCTR3) # Frame rate control. + self._writedata(data6) + time.sleep_us(10) + + data1 = bytearray(1) + self._writecommand(TFT.INVCTR) # Display inversion control + data1[0] = 0x07 # Line inversion. + self._writedata(data1) + + self._writecommand(TFT.PWCTR1) # Power control + data3[0] = 0xA2 + data3[1] = 0x02 + data3[2] = 0x84 + self._writedata(data3) + + self._writecommand(TFT.PWCTR2) # Power control + data1[0] = 0xC5 # VGH = 14.7V, VGL = -7.35V + self._writedata(data1) + + data2 = bytearray(2) + self._writecommand(TFT.PWCTR3) # Power control + data2[0] = 0x0A # Opamp current small + data2[1] = 0x00 # Boost frequency + self._writedata(data2) + + self._writecommand(TFT.PWCTR4) # Power control + data2[0] = 0x8A # Opamp current small + data2[1] = 0x2A # Boost frequency + self._writedata(data2) + + self._writecommand(TFT.PWCTR5) # Power control + data2[0] = 0x8A # Opamp current small + data2[1] = 0xEE # Boost frequency + self._writedata(data2) + + self._writecommand(TFT.VMCTR1) # Power control + data1[0] = 0x0E + self._writedata(data1) + + self._writecommand(TFT.INVOFF) + + self._writecommand(TFT.MADCTL) # Power control + data1[0] = 0xC8 + self._writedata(data1) + + self._writecommand(TFT.COLMOD) + data1[0] = 0x05 + self._writedata(data1) + + self._writecommand(TFT.CASET) # Column address set. + self.windowLocData[0] = 0x00 + self.windowLocData[1] = 0x00 + self.windowLocData[2] = 0x00 + self.windowLocData[3] = self._size[0] - 1 + self._writedata(self.windowLocData) + + self._writecommand(TFT.RASET) # Row address set. + self.windowLocData[3] = self._size[1] - 1 + self._writedata(self.windowLocData) + + dataGMCTRP = bytearray([0x0f, 0x1a, 0x0f, 0x18, 0x2f, 0x28, 0x20, 0x22, 0x1f, + 0x1b, 0x23, 0x37, 0x00, 0x07, 0x02, 0x10]) + self._writecommand(TFT.GMCTRP1) + self._writedata(dataGMCTRP) + + dataGMCTRN = bytearray([0x0f, 0x1b, 0x0f, 0x17, 0x33, 0x2c, 0x29, 0x2e, 0x30, + 0x30, 0x39, 0x3f, 0x00, 0x07, 0x03, 0x10]) + self._writecommand(TFT.GMCTRN1) + self._writedata(dataGMCTRN) + time.sleep_us(10) + + self._writecommand(TFT.DISPON) + time.sleep_us(100) + + self._writecommand(TFT.NORON) # Normal display on. + time.sleep_us(10) + + self.cs(1) + + def initb2(self): + '''Initialize another blue tab version.''' + self._size = (ScreenSize[0] + 2, ScreenSize[1] + 1) + self._offset[0] = 2 + self._offset[1] = 1 + self._reset() + self._writecommand(TFT.SWRESET) # Software reset. + time.sleep_us(50) + self._writecommand(TFT.SLPOUT) # out of sleep mode. + time.sleep_us(500) + + data3 = bytearray([0x01, 0x2C, 0x2D]) # + self._writecommand(TFT.FRMCTR1) # Frame rate control. + self._writedata(data3) + time.sleep_us(10) + + self._writecommand(TFT.FRMCTR2) # Frame rate control. + self._writedata(data3) + time.sleep_us(10) + + self._writecommand(TFT.FRMCTR3) # Frame rate control. + self._writedata(data3) + time.sleep_us(10) + + self._writecommand(TFT.INVCTR) # Display inversion control + data1 = bytearray(1) # + data1[0] = 0x07 + self._writedata(data1) + + self._writecommand(TFT.PWCTR1) # Power control + data3[0] = 0xA2 # + data3[1] = 0x02 # + data3[2] = 0x84 # + self._writedata(data3) + time.sleep_us(10) + + self._writecommand(TFT.PWCTR2) # Power control + data1[0] = 0xC5 # + self._writedata(data1) + + self._writecommand(TFT.PWCTR3) # Power control + data2 = bytearray(2) + data2[0] = 0x0A # + data2[1] = 0x00 # + self._writedata(data2) + + self._writecommand(TFT.PWCTR4) # Power control + data2[0] = 0x8A # + data2[1] = 0x2A # + self._writedata(data2) + + self._writecommand(TFT.PWCTR5) # Power control + data2[0] = 0x8A # + data2[1] = 0xEE # + self._writedata(data2) + + self._writecommand(TFT.VMCTR1) # Power control + data1[0] = 0x0E # + self._writedata(data1) + time.sleep_us(10) + + self._writecommand(TFT.MADCTL) + data1[0] = 0xC8 # row address/col address, bottom to top refresh + self._writedata(data1) + + # These different values don't seem to make a difference. + # dataGMCTRP = bytearray([0x0f, 0x1a, 0x0f, 0x18, 0x2f, 0x28, 0x20, 0x22, 0x1f, + # 0x1b, 0x23, 0x37, 0x00, 0x07, 0x02, 0x10]) + dataGMCTRP = bytearray([0x02, 0x1c, 0x07, 0x12, 0x37, 0x32, 0x29, 0x2d, 0x29, + 0x25, 0x2b, 0x39, 0x00, 0x01, 0x03, 0x10]) + self._writecommand(TFT.GMCTRP1) + self._writedata(dataGMCTRP) + + # dataGMCTRN = bytearray([0x0f, 0x1b, 0x0f, 0x17, 0x33, 0x2c, 0x29, 0x2e, 0x30, + # 0x30, 0x39, 0x3f, 0x00, 0x07, 0x03, 0x10]) + dataGMCTRN = bytearray([0x03, 0x1d, 0x07, 0x06, 0x2e, 0x2c, 0x29, 0x2d, 0x2e, + 0x2e, 0x37, 0x3f, 0x00, 0x00, 0x02, 0x10]) + self._writecommand(TFT.GMCTRN1) + self._writedata(dataGMCTRN) + time.sleep_us(10) + + self._writecommand(TFT.CASET) # Column address set. + self.windowLocData[0] = 0x00 + self.windowLocData[1] = 0x02 # Start at column 2 + self.windowLocData[2] = 0x00 + self.windowLocData[3] = self._size[0] - 1 + self._writedata(self.windowLocData) + + self._writecommand(TFT.RASET) # Row address set. + self.windowLocData[1] = 0x01 # Start at row 2. + self.windowLocData[3] = self._size[1] - 1 + self._writedata(self.windowLocData) + + data1 = bytearray(1) + self._writecommand(TFT.COLMOD) # Set color mode. + data1[0] = 0x05 # 16 bit color. + self._writedata(data1) + time.sleep_us(10) + + self._writecommand(TFT.NORON) # Normal display on. + time.sleep_us(10) + + self._writecommand(TFT.RAMWR) + time.sleep_us(500) + + self._writecommand(TFT.DISPON) + self.cs(1) + time.sleep_us(500) + + # @micropython.native + def initg(self): + '''Initialize a green tab version.''' + self._reset() + + self._writecommand(TFT.SWRESET) # Software reset. + time.sleep_us(150) + self._writecommand(TFT.SLPOUT) # out of sleep mode. + time.sleep_us(255) + + data3 = bytearray([0x01, 0x2C, 0x2D]) # fastest refresh, 6 lines front, 3 lines back. + self._writecommand(TFT.FRMCTR1) # Frame rate control. + self._writedata(data3) + + self._writecommand(TFT.FRMCTR2) # Frame rate control. + self._writedata(data3) + + data6 = bytearray([0x01, 0x2c, 0x2d, 0x01, 0x2c, 0x2d]) + self._writecommand(TFT.FRMCTR3) # Frame rate control. + self._writedata(data6) + time.sleep_us(10) + + self._writecommand(TFT.INVCTR) # Display inversion control + self._writedata(bytearray([0x07])) + self._writecommand(TFT.PWCTR1) # Power control + data3[0] = 0xA2 + data3[1] = 0x02 + data3[2] = 0x84 + self._writedata(data3) + + self._writecommand(TFT.PWCTR2) # Power control + self._writedata(bytearray([0xC5])) + + data2 = bytearray(2) + self._writecommand(TFT.PWCTR3) # Power control + data2[0] = 0x0A # Opamp current small + data2[1] = 0x00 # Boost frequency + self._writedata(data2) + + self._writecommand(TFT.PWCTR4) # Power control + data2[0] = 0x8A # Opamp current small + data2[1] = 0x2A # Boost frequency + self._writedata(data2) + + self._writecommand(TFT.PWCTR5) # Power control + data2[0] = 0x8A # Opamp current small + data2[1] = 0xEE # Boost frequency + self._writedata(data2) + + self._writecommand(TFT.VMCTR1) # Power control + self._writedata(bytearray([0x0E])) + + self._writecommand(TFT.INVOFF) + + self._setMADCTL() + + self._writecommand(TFT.COLMOD) + self._writedata(bytearray([0x05])) + + self._writecommand(TFT.CASET) # Column address set. + self.windowLocData[0] = 0x00 + self.windowLocData[1] = 0x01 # Start at row/column 1. + self.windowLocData[2] = 0x00 + self.windowLocData[3] = self._size[0] - 1 + self._writedata(self.windowLocData) + + self._writecommand(TFT.RASET) # Row address set. + self.windowLocData[3] = self._size[1] - 1 + self._writedata(self.windowLocData) + + dataGMCTRP = bytearray([0x02, 0x1c, 0x07, 0x12, 0x37, 0x32, 0x29, 0x2d, 0x29, + 0x25, 0x2b, 0x39, 0x00, 0x01, 0x03, 0x10]) + self._writecommand(TFT.GMCTRP1) + self._writedata(dataGMCTRP) + + dataGMCTRN = bytearray([0x03, 0x1d, 0x07, 0x06, 0x2e, 0x2c, 0x29, 0x2d, 0x2e, + 0x2e, 0x37, 0x3f, 0x00, 0x00, 0x02, 0x10]) + self._writecommand(TFT.GMCTRN1) + self._writedata(dataGMCTRN) + + self._writecommand(TFT.NORON) # Normal display on. + time.sleep_us(10) + + self._writecommand(TFT.DISPON) + time.sleep_us(100) + + self.cs(1) + + +def maker(): + t = TFT(1, "X1", "X2") + print("Initializing") + t.initr() + t.fill(0) + return t + + +def makeb(): + t = TFT(1, "X1", "X2") + print("Initializing") + t.initb() + t.fill(0) + return t + + +def makeg(): + t = TFT(1, "X1", "X2") + print("Initializing") + t.initg() + t.fill(0) + return t \ No newline at end of file diff --git a/sysfont.py b/sysfont.py new file mode 100644 index 0000000..d10c4fa --- /dev/null +++ b/sysfont.py @@ -0,0 +1,263 @@ +#Font used for ST7735 display. + +#Each character uses 5 bytes. +#index using ASCII value * 5. +#Each byte contains a column of pixels. +#The character may be 8 pixels high and 5 wide. + +sysfont = {"Width": 5, "Height": 8, "Start": 0, "End": 254, "Data": bytearray([ + 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3E, 0x5B, 0x4F, 0x5B, 0x3E, + 0x3E, 0x6B, 0x4F, 0x6B, 0x3E, + 0x1C, 0x3E, 0x7C, 0x3E, 0x1C, + 0x18, 0x3C, 0x7E, 0x3C, 0x18, + 0x1C, 0x57, 0x7D, 0x57, 0x1C, + 0x1C, 0x5E, 0x7F, 0x5E, 0x1C, + 0x00, 0x18, 0x3C, 0x18, 0x00, + 0xFF, 0xE7, 0xC3, 0xE7, 0xFF, + 0x00, 0x18, 0x24, 0x18, 0x00, + 0xFF, 0xE7, 0xDB, 0xE7, 0xFF, + 0x30, 0x48, 0x3A, 0x06, 0x0E, + 0x26, 0x29, 0x79, 0x29, 0x26, + 0x40, 0x7F, 0x05, 0x05, 0x07, + 0x40, 0x7F, 0x05, 0x25, 0x3F, + 0x5A, 0x3C, 0xE7, 0x3C, 0x5A, + 0x7F, 0x3E, 0x1C, 0x1C, 0x08, + 0x08, 0x1C, 0x1C, 0x3E, 0x7F, + 0x14, 0x22, 0x7F, 0x22, 0x14, + 0x5F, 0x5F, 0x00, 0x5F, 0x5F, + 0x06, 0x09, 0x7F, 0x01, 0x7F, + 0x00, 0x66, 0x89, 0x95, 0x6A, + 0x60, 0x60, 0x60, 0x60, 0x60, + 0x94, 0xA2, 0xFF, 0xA2, 0x94, + 0x08, 0x04, 0x7E, 0x04, 0x08, + 0x10, 0x20, 0x7E, 0x20, 0x10, + 0x08, 0x08, 0x2A, 0x1C, 0x08, + 0x08, 0x1C, 0x2A, 0x08, 0x08, + 0x1E, 0x10, 0x10, 0x10, 0x10, + 0x0C, 0x1E, 0x0C, 0x1E, 0x0C, + 0x30, 0x38, 0x3E, 0x38, 0x30, + 0x06, 0x0E, 0x3E, 0x0E, 0x06, + 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x5F, 0x00, 0x00, + 0x00, 0x07, 0x00, 0x07, 0x00, + 0x14, 0x7F, 0x14, 0x7F, 0x14, + 0x24, 0x2A, 0x7F, 0x2A, 0x12, + 0x23, 0x13, 0x08, 0x64, 0x62, + 0x36, 0x49, 0x56, 0x20, 0x50, + 0x00, 0x08, 0x07, 0x03, 0x00, + 0x00, 0x1C, 0x22, 0x41, 0x00, + 0x00, 0x41, 0x22, 0x1C, 0x00, + 0x2A, 0x1C, 0x7F, 0x1C, 0x2A, + 0x08, 0x08, 0x3E, 0x08, 0x08, + 0x00, 0x80, 0x70, 0x30, 0x00, + 0x08, 0x08, 0x08, 0x08, 0x08, + 0x00, 0x00, 0x60, 0x60, 0x00, + 0x20, 0x10, 0x08, 0x04, 0x02, + 0x3E, 0x51, 0x49, 0x45, 0x3E, + 0x00, 0x42, 0x7F, 0x40, 0x00, + 0x72, 0x49, 0x49, 0x49, 0x46, + 0x21, 0x41, 0x49, 0x4D, 0x33, + 0x18, 0x14, 0x12, 0x7F, 0x10, + 0x27, 0x45, 0x45, 0x45, 0x39, + 0x3C, 0x4A, 0x49, 0x49, 0x31, + 0x41, 0x21, 0x11, 0x09, 0x07, + 0x36, 0x49, 0x49, 0x49, 0x36, + 0x46, 0x49, 0x49, 0x29, 0x1E, + 0x00, 0x00, 0x14, 0x00, 0x00, + 0x00, 0x40, 0x34, 0x00, 0x00, + 0x00, 0x08, 0x14, 0x22, 0x41, + 0x14, 0x14, 0x14, 0x14, 0x14, + 0x00, 0x41, 0x22, 0x14, 0x08, + 0x02, 0x01, 0x59, 0x09, 0x06, + 0x3E, 0x41, 0x5D, 0x59, 0x4E, + 0x7C, 0x12, 0x11, 0x12, 0x7C, + 0x7F, 0x49, 0x49, 0x49, 0x36, + 0x3E, 0x41, 0x41, 0x41, 0x22, + 0x7F, 0x41, 0x41, 0x41, 0x3E, + 0x7F, 0x49, 0x49, 0x49, 0x41, + 0x7F, 0x09, 0x09, 0x09, 0x01, + 0x3E, 0x41, 0x41, 0x51, 0x73, + 0x7F, 0x08, 0x08, 0x08, 0x7F, + 0x00, 0x41, 0x7F, 0x41, 0x00, + 0x20, 0x40, 0x41, 0x3F, 0x01, + 0x7F, 0x08, 0x14, 0x22, 0x41, + 0x7F, 0x40, 0x40, 0x40, 0x40, + 0x7F, 0x02, 0x1C, 0x02, 0x7F, + 0x7F, 0x04, 0x08, 0x10, 0x7F, + 0x3E, 0x41, 0x41, 0x41, 0x3E, + 0x7F, 0x09, 0x09, 0x09, 0x06, + 0x3E, 0x41, 0x51, 0x21, 0x5E, + 0x7F, 0x09, 0x19, 0x29, 0x46, + 0x26, 0x49, 0x49, 0x49, 0x32, + 0x03, 0x01, 0x7F, 0x01, 0x03, + 0x3F, 0x40, 0x40, 0x40, 0x3F, + 0x1F, 0x20, 0x40, 0x20, 0x1F, + 0x3F, 0x40, 0x38, 0x40, 0x3F, + 0x63, 0x14, 0x08, 0x14, 0x63, + 0x03, 0x04, 0x78, 0x04, 0x03, + 0x61, 0x59, 0x49, 0x4D, 0x43, + 0x00, 0x7F, 0x41, 0x41, 0x41, + 0x02, 0x04, 0x08, 0x10, 0x20, + 0x00, 0x41, 0x41, 0x41, 0x7F, + 0x04, 0x02, 0x01, 0x02, 0x04, + 0x40, 0x40, 0x40, 0x40, 0x40, + 0x00, 0x03, 0x07, 0x08, 0x00, + 0x20, 0x54, 0x54, 0x78, 0x40, + 0x7F, 0x28, 0x44, 0x44, 0x38, + 0x38, 0x44, 0x44, 0x44, 0x28, + 0x38, 0x44, 0x44, 0x28, 0x7F, + 0x38, 0x54, 0x54, 0x54, 0x18, + 0x00, 0x08, 0x7E, 0x09, 0x02, + 0x18, 0xA4, 0xA4, 0x9C, 0x78, + 0x7F, 0x08, 0x04, 0x04, 0x78, + 0x00, 0x44, 0x7D, 0x40, 0x00, + 0x20, 0x40, 0x40, 0x3D, 0x00, + 0x7F, 0x10, 0x28, 0x44, 0x00, + 0x00, 0x41, 0x7F, 0x40, 0x00, + 0x7C, 0x04, 0x78, 0x04, 0x78, + 0x7C, 0x08, 0x04, 0x04, 0x78, + 0x38, 0x44, 0x44, 0x44, 0x38, + 0xFC, 0x18, 0x24, 0x24, 0x18, + 0x18, 0x24, 0x24, 0x18, 0xFC, + 0x7C, 0x08, 0x04, 0x04, 0x08, + 0x48, 0x54, 0x54, 0x54, 0x24, + 0x04, 0x04, 0x3F, 0x44, 0x24, + 0x3C, 0x40, 0x40, 0x20, 0x7C, + 0x1C, 0x20, 0x40, 0x20, 0x1C, + 0x3C, 0x40, 0x30, 0x40, 0x3C, + 0x44, 0x28, 0x10, 0x28, 0x44, + 0x4C, 0x90, 0x90, 0x90, 0x7C, + 0x44, 0x64, 0x54, 0x4C, 0x44, + 0x00, 0x08, 0x36, 0x41, 0x00, + 0x00, 0x00, 0x77, 0x00, 0x00, + 0x00, 0x41, 0x36, 0x08, 0x00, + 0x02, 0x01, 0x02, 0x04, 0x02, + 0x3C, 0x26, 0x23, 0x26, 0x3C, + 0x1E, 0xA1, 0xA1, 0x61, 0x12, + 0x3A, 0x40, 0x40, 0x20, 0x7A, + 0x38, 0x54, 0x54, 0x55, 0x59, + 0x21, 0x55, 0x55, 0x79, 0x41, + 0x21, 0x54, 0x54, 0x78, 0x41, + 0x21, 0x55, 0x54, 0x78, 0x40, + 0x20, 0x54, 0x55, 0x79, 0x40, + 0x0C, 0x1E, 0x52, 0x72, 0x12, + 0x39, 0x55, 0x55, 0x55, 0x59, + 0x39, 0x54, 0x54, 0x54, 0x59, + 0x39, 0x55, 0x54, 0x54, 0x58, + 0x00, 0x00, 0x45, 0x7C, 0x41, + 0x00, 0x02, 0x45, 0x7D, 0x42, + 0x00, 0x01, 0x45, 0x7C, 0x40, + 0xF0, 0x29, 0x24, 0x29, 0xF0, + 0xF0, 0x28, 0x25, 0x28, 0xF0, + 0x7C, 0x54, 0x55, 0x45, 0x00, + 0x20, 0x54, 0x54, 0x7C, 0x54, + 0x7C, 0x0A, 0x09, 0x7F, 0x49, + 0x32, 0x49, 0x49, 0x49, 0x32, + 0x32, 0x48, 0x48, 0x48, 0x32, + 0x32, 0x4A, 0x48, 0x48, 0x30, + 0x3A, 0x41, 0x41, 0x21, 0x7A, + 0x3A, 0x42, 0x40, 0x20, 0x78, + 0x00, 0x9D, 0xA0, 0xA0, 0x7D, + 0x39, 0x44, 0x44, 0x44, 0x39, + 0x3D, 0x40, 0x40, 0x40, 0x3D, + 0x3C, 0x24, 0xFF, 0x24, 0x24, + 0x48, 0x7E, 0x49, 0x43, 0x66, + 0x2B, 0x2F, 0xFC, 0x2F, 0x2B, + 0xFF, 0x09, 0x29, 0xF6, 0x20, + 0xC0, 0x88, 0x7E, 0x09, 0x03, + 0x20, 0x54, 0x54, 0x79, 0x41, + 0x00, 0x00, 0x44, 0x7D, 0x41, + 0x30, 0x48, 0x48, 0x4A, 0x32, + 0x38, 0x40, 0x40, 0x22, 0x7A, + 0x00, 0x7A, 0x0A, 0x0A, 0x72, + 0x7D, 0x0D, 0x19, 0x31, 0x7D, + 0x26, 0x29, 0x29, 0x2F, 0x28, + 0x26, 0x29, 0x29, 0x29, 0x26, + 0x30, 0x48, 0x4D, 0x40, 0x20, + 0x38, 0x08, 0x08, 0x08, 0x08, + 0x08, 0x08, 0x08, 0x08, 0x38, + 0x2F, 0x10, 0xC8, 0xAC, 0xBA, + 0x2F, 0x10, 0x28, 0x34, 0xFA, + 0x00, 0x00, 0x7B, 0x00, 0x00, + 0x08, 0x14, 0x2A, 0x14, 0x22, + 0x22, 0x14, 0x2A, 0x14, 0x08, + 0xAA, 0x00, 0x55, 0x00, 0xAA, + 0xAA, 0x55, 0xAA, 0x55, 0xAA, + 0x00, 0x00, 0x00, 0xFF, 0x00, + 0x10, 0x10, 0x10, 0xFF, 0x00, + 0x14, 0x14, 0x14, 0xFF, 0x00, + 0x10, 0x10, 0xFF, 0x00, 0xFF, + 0x10, 0x10, 0xF0, 0x10, 0xF0, + 0x14, 0x14, 0x14, 0xFC, 0x00, + 0x14, 0x14, 0xF7, 0x00, 0xFF, + 0x00, 0x00, 0xFF, 0x00, 0xFF, + 0x14, 0x14, 0xF4, 0x04, 0xFC, + 0x14, 0x14, 0x17, 0x10, 0x1F, + 0x10, 0x10, 0x1F, 0x10, 0x1F, + 0x14, 0x14, 0x14, 0x1F, 0x00, + 0x10, 0x10, 0x10, 0xF0, 0x00, + 0x00, 0x00, 0x00, 0x1F, 0x10, + 0x10, 0x10, 0x10, 0x1F, 0x10, + 0x10, 0x10, 0x10, 0xF0, 0x10, + 0x00, 0x00, 0x00, 0xFF, 0x10, + 0x10, 0x10, 0x10, 0x10, 0x10, + 0x10, 0x10, 0x10, 0xFF, 0x10, + 0x00, 0x00, 0x00, 0xFF, 0x14, + 0x00, 0x00, 0xFF, 0x00, 0xFF, + 0x00, 0x00, 0x1F, 0x10, 0x17, + 0x00, 0x00, 0xFC, 0x04, 0xF4, + 0x14, 0x14, 0x17, 0x10, 0x17, + 0x14, 0x14, 0xF4, 0x04, 0xF4, + 0x00, 0x00, 0xFF, 0x00, 0xF7, + 0x14, 0x14, 0x14, 0x14, 0x14, + 0x14, 0x14, 0xF7, 0x00, 0xF7, + 0x14, 0x14, 0x14, 0x17, 0x14, + 0x10, 0x10, 0x1F, 0x10, 0x1F, + 0x14, 0x14, 0x14, 0xF4, 0x14, + 0x10, 0x10, 0xF0, 0x10, 0xF0, + 0x00, 0x00, 0x1F, 0x10, 0x1F, + 0x00, 0x00, 0x00, 0x1F, 0x14, + 0x00, 0x00, 0x00, 0xFC, 0x14, + 0x00, 0x00, 0xF0, 0x10, 0xF0, + 0x10, 0x10, 0xFF, 0x10, 0xFF, + 0x14, 0x14, 0x14, 0xFF, 0x14, + 0x10, 0x10, 0x10, 0x1F, 0x00, + 0x00, 0x00, 0x00, 0xF0, 0x10, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, + 0xFF, 0xFF, 0xFF, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xFF, 0xFF, + 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, + 0x38, 0x44, 0x44, 0x38, 0x44, + 0x7C, 0x2A, 0x2A, 0x3E, 0x14, + 0x7E, 0x02, 0x02, 0x06, 0x06, + 0x02, 0x7E, 0x02, 0x7E, 0x02, + 0x63, 0x55, 0x49, 0x41, 0x63, + 0x38, 0x44, 0x44, 0x3C, 0x04, + 0x40, 0x7E, 0x20, 0x1E, 0x20, + 0x06, 0x02, 0x7E, 0x02, 0x02, + 0x99, 0xA5, 0xE7, 0xA5, 0x99, + 0x1C, 0x2A, 0x49, 0x2A, 0x1C, + 0x4C, 0x72, 0x01, 0x72, 0x4C, + 0x30, 0x4A, 0x4D, 0x4D, 0x30, + 0x30, 0x48, 0x78, 0x48, 0x30, + 0xBC, 0x62, 0x5A, 0x46, 0x3D, + 0x3E, 0x49, 0x49, 0x49, 0x00, + 0x7E, 0x01, 0x01, 0x01, 0x7E, + 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, + 0x44, 0x44, 0x5F, 0x44, 0x44, + 0x40, 0x51, 0x4A, 0x44, 0x40, + 0x40, 0x44, 0x4A, 0x51, 0x40, + 0x00, 0x00, 0xFF, 0x01, 0x03, + 0xE0, 0x80, 0xFF, 0x00, 0x00, + 0x08, 0x08, 0x6B, 0x6B, 0x08, + 0x36, 0x12, 0x36, 0x24, 0x36, + 0x06, 0x0F, 0x09, 0x0F, 0x06, + 0x00, 0x00, 0x18, 0x18, 0x00, + 0x00, 0x00, 0x10, 0x10, 0x00, + 0x30, 0x40, 0xFF, 0x01, 0x01, + 0x00, 0x1F, 0x01, 0x01, 0x1E, + 0x00, 0x19, 0x1D, 0x17, 0x12, + 0x00, 0x3C, 0x3C, 0x3C, 0x3C +])} \ No newline at end of file diff --git a/widgets.py b/widgets.py new file mode 100644 index 0000000..0d4e62a --- /dev/null +++ b/widgets.py @@ -0,0 +1,90 @@ +from colors import * +from sysfont import sysfont + +class Widget: + def __init__(self, pos, callback=None, bg_color=RED): + self.has_focus = False + self.pos = pos + self.size = (0,0) + self.orig_pos = pos + self.callback = callback + self.back_color = bg_color + + + def draw(self, display): + if self.has_focus: + display.line(self.orig_pos, (self.orig_pos[0]+10, self.orig_pos[1]+8), RED) + display.line(self.orig_pos, (self.orig_pos[0], self.orig_pos[1]+16), RED) + display.line((self.orig_pos[0], self.orig_pos[1]+16), (self.orig_pos[0]+10, self.orig_pos[1]+8), RED) + display.fillrect(self.pos, (100, 100), NAVY) + if self.back_color is not None: + display.fillrect(self.pos, self.size, self.back_color) + + def has_focus(self): + return self.has_focus + + def set_focus(self, focus): + self.has_focus = focus + if self.has_focus: + self.pos = (self.orig_pos[0] + 12, self.orig_pos[1]) + else: + self.pos = self.orig_pos + + +class Label(Widget): + + def __init__(self, text, pos: tuple[int, int], text_offset:tuple[int, int]=None, f_color=BLACK, back_color=0, f_size=2, nowrap=True): + super().__init__(pos, bg_color=back_color) + self.ch_w = 5 + self.ch_h = 8 + self.text = text + self.text_offset = (0, 0) + if text_offset is not None: + self.text_offset = text_offset + self.f_color = f_color + self.f_size = f_size + self.nowrap = nowrap + self.size = (len(self.text)*(self.f_size*self.ch_w)+self.text_offset[0]+self.ch_w*self.f_size, self.f_size*self.ch_h+self.text_offset[1]) + + def draw(self, display): + super().draw(display) + display.text((self.pos[0] + self.text_offset[0], self.pos[1] + self.text_offset[1]), self.text, self.f_color, sysfont, self.f_size, nowrap=self.nowrap) + +class Checkbox(Label): + + def __init__(self, text, pos: tuple[int, int], checked=False, f_color=BLACK, back_color=0, f_size=2, nowrap=True): + self.ch_w = 5 + self.ch_h = 8 + self.b_pos = pos + self.box_size = (self.ch_w * f_size, self.ch_h * f_size) + self.checked = checked + super().__init__(text, pos, (self.box_size[0]+self.ch_w, 0), f_color, back_color, f_size, nowrap) + + def draw(self, display): + super().draw(display) + display.rect(self.b_pos, self.box_size, self.f_color) + if self.checked: + display.line(self.b_pos, (self.b_pos[0]+self.box_size[0]//2, self.b_pos[1]+self.box_size[1]), self.f_color) + display.line((self.b_pos[0]+self.box_size[0]//2, self.b_pos[1]+self.box_size[1]),\ + (self.b_pos[0]+self.box_size[0]+self.ch_w//2, self.b_pos[1]-self.ch_h//2), self.f_color) + + +class RadioButton(Label): + + def __init__(self, text, pos: tuple[int, int], checked=False, f_color=BLACK, back_color=0, f_size=2, nowrap=True): + self.ch_w = 5 + self.ch_h = 8 + self.b_pos = pos + self.box_size = (self.ch_w * f_size, self.ch_h * f_size) + self.checked = checked + super().__init__(text, pos, (self.box_size[0]+self.ch_w, 0), f_color, back_color, f_size, nowrap) + + def draw(self, display): + super().draw(display) + display.circle((self.b_pos[0]+self.ch_w*self.f_size//2, self.b_pos[1]+self.ch_w*self.f_size//2), + self.box_size[0]*0.75, self.f_color) + if self.checked: + display.circle((self.b_pos[0] + self.ch_w * self.f_size // 2, self.b_pos[1] + self.ch_w * self.f_size // 2), + self.box_size[0]*0.75, self.f_color) + display.fillcircle((self.b_pos[0] + self.ch_w * self.f_size // 2, self.b_pos[1] + self.ch_w * self.f_size // 2), + self.box_size[0]//2, self.f_color) \ No newline at end of file