kopia lustrzana https://github.com/peterhinch/micropython-samples
TFT GUI version 0.1
rodzic
d2f4e9e79b
commit
ceda9894b4
|
@ -1,25 +1,42 @@
|
||||||
|
# button.py Pushbutton classes for Pybboard TFT GUI
|
||||||
|
|
||||||
|
# The MIT License (MIT)
|
||||||
|
#
|
||||||
|
# Copyright (c) 2016 Peter Hinch
|
||||||
|
#
|
||||||
|
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
# of this software and associated documentation files (the "Software"), to deal
|
||||||
|
# in the Software without restriction, including without limitation the rights
|
||||||
|
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
# copies of the Software, and to permit persons to whom the Software is
|
||||||
|
# furnished to do so, subject to the following conditions:
|
||||||
|
#
|
||||||
|
# The above copyright notice and this permission notice shall be included in
|
||||||
|
# all copies or substantial portions of the Software.
|
||||||
|
#
|
||||||
|
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
# THE SOFTWARE.
|
||||||
|
|
||||||
from delay import Delay
|
from delay import Delay
|
||||||
from ui import get_stringsize, print_centered, touchable, CIRCLE, RECTANGLE, CLIPPED_RECT
|
from ui import get_stringsize, print_centered, Touchable, CIRCLE, RECTANGLE, CLIPPED_RECT
|
||||||
|
|
||||||
# Button coordinates relate to bounding box (BB). x, y are of BB top left corner.
|
# Button coordinates relate to bounding box (BB). x, y are of BB top left corner.
|
||||||
# likewise width and height refer to BB, regardless of button shape
|
# likewise width and height refer to BB, regardless of button shape
|
||||||
|
# If font is None button will be rendered without text
|
||||||
|
|
||||||
class Button(touchable):
|
class Button(Touchable):
|
||||||
def __init__(self, objsched, tft, objtouch, location, *, shape=CIRCLE, height=50, width=50, fill=True,
|
def __init__(self, objsched, tft, objtouch, location, *, font, shape=CIRCLE, height=50, width=50, fill=True,
|
||||||
fgcolor=None, bgcolor=None, fontcolor=None, litcolor=None, text='', font=None, show=True, callback=lambda x : None,
|
fgcolor=None, bgcolor=None, fontcolor=None, litcolor=None, text='', show=True, callback=lambda x, y : None,
|
||||||
args=[]):
|
args=[]):
|
||||||
super().__init__(objsched, objtouch)
|
super().__init__(objsched, tft, objtouch, location, font, height, width, fgcolor, bgcolor, fontcolor, None)
|
||||||
self.objsched = objsched
|
|
||||||
self.tft = tft
|
|
||||||
self.location = location
|
|
||||||
self.shape = shape
|
self.shape = shape
|
||||||
self.height = height
|
|
||||||
self.width = width
|
|
||||||
self.radius = height // 2
|
self.radius = height // 2
|
||||||
self.fill = fill
|
self.fill = fill
|
||||||
self.fgcolor = fgcolor
|
|
||||||
self.bgcolor = bgcolor
|
|
||||||
self.font = font if font is not None else tft.text_font
|
|
||||||
self.fontcolor = fontcolor if fontcolor is not None else tft.getColor()
|
|
||||||
self.litcolor = litcolor
|
self.litcolor = litcolor
|
||||||
self.text = text
|
self.text = text
|
||||||
self.callback = callback
|
self.callback = callback
|
||||||
|
@ -35,19 +52,14 @@ class Button(touchable):
|
||||||
|
|
||||||
def show(self):
|
def show(self):
|
||||||
tft = self.tft
|
tft = self.tft
|
||||||
fgcolor = tft.getColor() # save old colors
|
|
||||||
bgcolor = tft.getBGColor()
|
|
||||||
x = self.location[0]
|
x = self.location[0]
|
||||||
y = self.location[1]
|
y = self.location[1]
|
||||||
if not self.visible: # erase the button
|
if not self.visible: # erase the button
|
||||||
tft.setColor(bgcolor)
|
self.set_color(self.bgcolor)
|
||||||
tft.fillRectangle(x, y, x + self.width, y + self.height)
|
tft.fillRectangle(x, y, x + self.width, y + self.height)
|
||||||
tft.setColor(fgcolor)
|
self.restore_color()
|
||||||
return
|
return
|
||||||
if self.fgcolor is not None:
|
self.set_color() # to foreground
|
||||||
tft.setColor(self.fgcolor)
|
|
||||||
if self.bgcolor is not None:
|
|
||||||
tft.setBGColor(self.bgcolor)
|
|
||||||
if self.shape == CIRCLE: # Button coords are of top left corner of bounding box
|
if self.shape == CIRCLE: # Button coords are of top left corner of bounding box
|
||||||
x += self.radius
|
x += self.radius
|
||||||
y += self.radius
|
y += self.radius
|
||||||
|
@ -74,8 +86,7 @@ class Button(touchable):
|
||||||
tft.drawClippedRectangle(x, y, x1, y1)
|
tft.drawClippedRectangle(x, y, x1, y1)
|
||||||
if self.font is not None and len(self.text):
|
if self.font is not None and len(self.text):
|
||||||
print_centered(tft, (x + x1) // 2, (y + y1) // 2, self.text, self.fontcolor, self.font)
|
print_centered(tft, (x + x1) // 2, (y + y1) // 2, self.text, self.fontcolor, self.font)
|
||||||
tft.setColor(fgcolor) # restore them
|
self.restore_color()
|
||||||
tft.setBGColor(bgcolor)
|
|
||||||
|
|
||||||
def shownormal(self):
|
def shownormal(self):
|
||||||
self.fgcolor = self.orig_fgcolor
|
self.fgcolor = self.orig_fgcolor
|
||||||
|
@ -169,3 +180,52 @@ class RadioButtons(Buttons):
|
||||||
but.fgcolor = but.orig_fgcolor
|
but.fgcolor = but.orig_fgcolor
|
||||||
but.show()
|
but.show()
|
||||||
self.user_callback(button, args) # user gets button with args they specified
|
self.user_callback(button, args) # user gets button with args they specified
|
||||||
|
|
||||||
|
|
||||||
|
class Checkbox(Touchable):
|
||||||
|
def __init__(self, objsched, tft, objtouch, location, *, height=30, fillcolor=None,
|
||||||
|
fgcolor=None, bgcolor=None, callback=lambda x, y : None, args=[], value=False, border=None):
|
||||||
|
super().__init__(objsched, tft, objtouch, location, None, height, height, fgcolor, bgcolor, None, border)
|
||||||
|
self.callback = callback
|
||||||
|
self.callback_args = args
|
||||||
|
self.fillcolor = fillcolor
|
||||||
|
self.busy = False
|
||||||
|
self.value = value
|
||||||
|
self.show()
|
||||||
|
|
||||||
|
def show(self):
|
||||||
|
tft = self.tft
|
||||||
|
bw = self.draw_border() # and background if required. Result is width of border
|
||||||
|
x = self.location[0] + bw
|
||||||
|
y = self.location[1] + bw
|
||||||
|
height = self.height - 2 * bw
|
||||||
|
x1 = x + height
|
||||||
|
y1 = y + height
|
||||||
|
if self.fillcolor is None or not self.value:
|
||||||
|
self.set_color(self.bgcolor) # blank
|
||||||
|
tft.fillRectangle(x, y, x1, y1)
|
||||||
|
if self.fillcolor is not None and self.value:
|
||||||
|
self.set_color(self.fillcolor)
|
||||||
|
tft.fillRectangle(x, y, x1, y1)
|
||||||
|
self.set_color()
|
||||||
|
tft.drawRectangle(x, y, x1, y1)
|
||||||
|
if self.fillcolor is None and self.value:
|
||||||
|
tft.drawLine(x, y, x1, y1)
|
||||||
|
tft.drawLine(x, y1, x1, y)
|
||||||
|
|
||||||
|
def touched(self, x, y): # If touched, process it otherwise do nothing
|
||||||
|
is_touched = False
|
||||||
|
x0 = self.location[0]
|
||||||
|
x1 = self.location[0] + self.width
|
||||||
|
y0 = self.location[1]
|
||||||
|
y1 = self.location[1] + self.height
|
||||||
|
if x0 <= x <= x1 and y0 <= y <= y1:
|
||||||
|
is_touched = True
|
||||||
|
if is_touched and not self.busy: # Respond once to a press
|
||||||
|
self.value = not self.value
|
||||||
|
self.callback(self, self.callback_args) # Callback not a bound method so pass self
|
||||||
|
self.busy = True # Ensure no response to continued press
|
||||||
|
self.show()
|
||||||
|
|
||||||
|
def untouched(self): # User has released touchpad or touched elsewhere
|
||||||
|
self.busy = False
|
||||||
|
|
|
@ -1,106 +1,137 @@
|
||||||
|
# buttontest.py Test/demo of pushbutton classes for Pybboard TFT GUI
|
||||||
|
|
||||||
|
# The MIT License (MIT)
|
||||||
|
#
|
||||||
|
# Copyright (c) 2016 Peter Hinch
|
||||||
|
#
|
||||||
|
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
# of this software and associated documentation files (the "Software"), to deal
|
||||||
|
# in the Software without restriction, including without limitation the rights
|
||||||
|
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
# copies of the Software, and to permit persons to whom the Software is
|
||||||
|
# furnished to do so, subject to the following conditions:
|
||||||
|
#
|
||||||
|
# The above copyright notice and this permission notice shall be included in
|
||||||
|
# all copies or substantial portions of the Software.
|
||||||
|
#
|
||||||
|
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
# THE SOFTWARE.
|
||||||
|
|
||||||
import gc
|
|
||||||
from font14 import font14
|
from font14 import font14
|
||||||
from tft import TFT, LANDSCAPE
|
from tft import TFT, LANDSCAPE
|
||||||
from usched import Sched
|
from usched import Sched
|
||||||
from touch import TOUCH
|
from touch import TOUCH
|
||||||
from button import Button, Buttonset, RadioButtons
|
from button import Button, Buttonset, RadioButtons, Checkbox
|
||||||
from ui import CIRCLE, RECTANGLE, CLIPPED_RECT
|
from ui import CIRCLE, RECTANGLE, CLIPPED_RECT, WHITE, BLACK, RED, GREEN, BLUE, YELLOW, GREY
|
||||||
#gc.collect()
|
from displays import Label
|
||||||
|
|
||||||
def callback(button, args):
|
def callback(button, args):
|
||||||
arg = args[0]
|
arg = args[0]
|
||||||
print('Returned: ', arg)
|
label = args[1]
|
||||||
tft = button.tft
|
label.show(arg)
|
||||||
tft.setTextPos(0, 240)
|
|
||||||
tft.setTextStyle(None, None, 0, font14)
|
|
||||||
tft.printString('Button argument zero: {} '.format(arg))
|
|
||||||
if arg == 'Q':
|
if arg == 'Q':
|
||||||
button.objsched.stop()
|
button.objsched.stop()
|
||||||
|
|
||||||
#gc.collect()
|
def cbcb(checkbox, args):
|
||||||
|
label = args[0]
|
||||||
|
if checkbox.value:
|
||||||
|
label.show('True')
|
||||||
|
else:
|
||||||
|
label.show('False')
|
||||||
|
|
||||||
# These tables contain args that differ between members of a set of related buttons
|
# These tables contain args that differ between members of a set of related buttons
|
||||||
table = [
|
table = [
|
||||||
{'fgcolor' : (0, 128, 0), 'litcolor' : (0, 255, 0), 'text' : 'Yes', 'args' : ('A'), 'fontcolor' : (0, 0, 0)},
|
{'fgcolor' : GREEN, 'text' : 'Yes', 'args' : ['A'], 'fontcolor' : (0, 0, 0)},
|
||||||
{'fgcolor' : (255, 0, 0), 'text' : 'No', 'args' : ('B')},
|
{'fgcolor' : RED, 'text' : 'No', 'args' : ['B']},
|
||||||
{'fgcolor' : (0, 0, 255), 'text' : '???', 'args' : ('C'), 'fill': False},
|
{'fgcolor' : BLUE, 'text' : '???', 'args' : ['C'], 'fill': False},
|
||||||
{'fgcolor' : (128, 128, 128), 'text' : 'Quit', 'args' : ('Q'), 'shape' : CLIPPED_RECT},
|
{'fgcolor' : GREY, 'text' : 'Quit', 'args' : ['Q'], 'shape' : CLIPPED_RECT},
|
||||||
]
|
]
|
||||||
|
|
||||||
# similar buttons: only tabulate data that varies
|
# similar buttons: only tabulate data that varies
|
||||||
table2 = [
|
table2 = [
|
||||||
{'text' : 'P', 'args' : ('p')},
|
{'text' : 'P', 'args' : ['p']},
|
||||||
{'text' : 'Q', 'args' : ('q')},
|
{'text' : 'Q', 'args' : ['q']},
|
||||||
{'text' : 'R', 'args' : ('r')},
|
{'text' : 'R', 'args' : ['r']},
|
||||||
{'text' : 'S', 'args' : ('s')},
|
{'text' : 'S', 'args' : ['s']},
|
||||||
]
|
]
|
||||||
|
|
||||||
# A Buttonset with two entries
|
# A Buttonset with two entries
|
||||||
# If buttons to be used in a buttonset, Use list rather than tuple for args because buttonset appends.
|
# If buttons to be used in a buttonset, Use list rather than tuple for args because buttonset appends.
|
||||||
|
|
||||||
table3 = [
|
table3 = [
|
||||||
{'fgcolor' : (0, 255, 0), 'shape' : CLIPPED_RECT, 'text' : 'Start', 'args' : ['Live']},
|
{'fgcolor' : GREEN, 'shape' : CLIPPED_RECT, 'text' : 'Start', 'args' : ['Live']},
|
||||||
{'fgcolor' : (255, 0, 0), 'shape' : CLIPPED_RECT, 'text' : 'Stop', 'args' : ['Die']},
|
{'fgcolor' : RED, 'shape' : CLIPPED_RECT, 'text' : 'Stop', 'args' : ['Die']},
|
||||||
]
|
]
|
||||||
|
|
||||||
table4 = [
|
table4 = [
|
||||||
{'text' : '1', 'args' : ('1')},
|
{'text' : '1', 'args' : ['1']},
|
||||||
{'text' : '2', 'args' : ('2')},
|
{'text' : '2', 'args' : ['2']},
|
||||||
{'text' : '3', 'args' : ('3')},
|
{'text' : '3', 'args' : ['3']},
|
||||||
{'text' : '4', 'args' : ('4')},
|
{'text' : '4', 'args' : ['4']},
|
||||||
]
|
]
|
||||||
|
|
||||||
#gc.collect()
|
labels = { 'width' : 70,
|
||||||
# THREADS
|
'fontcolor' : WHITE,
|
||||||
|
'border' : 2,
|
||||||
def stop(fTim, objsched): # Stop the scheduler after fTim seconds
|
'fgcolor' : RED,
|
||||||
yield fTim
|
'bgcolor' : (0, 40, 0),
|
||||||
objsched.stop()
|
'font' : font14,
|
||||||
|
}
|
||||||
|
|
||||||
# USER TEST FUNCTION
|
# USER TEST FUNCTION
|
||||||
|
|
||||||
def test(duration = 0):
|
def test():
|
||||||
if duration:
|
|
||||||
print("Test TFT panel for {:3d} seconds".format(duration))
|
|
||||||
else:
|
|
||||||
print('Testing TFT...')
|
print('Testing TFT...')
|
||||||
objsched = Sched() # Instantiate the scheduler
|
objsched = Sched() # Instantiate the scheduler
|
||||||
mytft = TFT("SSD1963", "LB04301", LANDSCAPE)
|
mytft = TFT("SSD1963", "LB04301", LANDSCAPE)
|
||||||
mytouch = TOUCH("XPT2046", objsched)
|
mytouch = TOUCH("XPT2046", objsched)
|
||||||
mytft.backlight(100) # light on
|
mytft.backlight(100) # light on
|
||||||
|
lstlbl = []
|
||||||
|
for n in range(3):
|
||||||
|
lstlbl.append(Label(mytft, (350, 50 * n), **labels))
|
||||||
|
|
||||||
# Button assortment
|
# Button assortment
|
||||||
x = 50
|
x = 0
|
||||||
for t in table:
|
for t in table:
|
||||||
|
t['args'].append(lstlbl[2])
|
||||||
Button(objsched, mytft, mytouch, (x, 0), font = font14, callback = callback, **t)
|
Button(objsched, mytft, mytouch, (x, 0), font = font14, callback = callback, **t)
|
||||||
x += 70
|
x += 70
|
||||||
|
|
||||||
# Highlighting buttons
|
# Highlighting buttons
|
||||||
x = 50
|
x = 0
|
||||||
for t in table2:
|
for t in table2:
|
||||||
Button(objsched, mytft, mytouch, (x, 60), font = font14, fgcolor = (128, 128, 128),
|
t['args'].append(lstlbl[2])
|
||||||
fontcolor = (0, 0, 0), litcolor = (255, 255, 255), callback = callback, **t)
|
Button(objsched, mytft, mytouch, (x, 60), fgcolor = GREY,
|
||||||
|
fontcolor = BLACK, litcolor = WHITE, font = font14, callback = callback, **t)
|
||||||
x += 70
|
x += 70
|
||||||
|
|
||||||
# On/Off toggle
|
# On/Off toggle
|
||||||
x = 50
|
x = 0
|
||||||
bs = Buttonset(callback)
|
bs = Buttonset(callback)
|
||||||
for t in table3: # Buttons overlay each other at same location
|
for t in table3: # Buttons overlay each other at same location
|
||||||
bs.add_button(objsched, mytft, mytouch, (x, 120), font = font14, fontcolor = (0, 0, 0), **t)
|
t['args'].append(lstlbl[2])
|
||||||
|
bs.add_button(objsched, mytft, mytouch, (x, 120), font = font14, fontcolor = BLACK, **t)
|
||||||
bs.run()
|
bs.run()
|
||||||
|
|
||||||
# Radio buttons
|
# Radio buttons
|
||||||
x = 50
|
x = 0
|
||||||
rb = RadioButtons(callback, (0, 0, 255)) # color of selected button
|
rb = RadioButtons(callback, BLUE) # color of selected button
|
||||||
for t in table4:
|
for t in table4:
|
||||||
rb.add_button(objsched, mytft, mytouch, (x, 180), font = font14, fontcolor = (255, 255, 255),
|
t['args'].append(lstlbl[2])
|
||||||
|
rb.add_button(objsched, mytft, mytouch, (x, 180), font = font14, fontcolor = WHITE,
|
||||||
fgcolor = (0, 0, 90), height = 30, **t)
|
fgcolor = (0, 0, 90), height = 30, **t)
|
||||||
x += 40
|
x += 40
|
||||||
rb.run()
|
rb.run()
|
||||||
|
|
||||||
# Start scheduler
|
# Checkbox
|
||||||
if duration:
|
Checkbox(objsched, mytft, mytouch, (300, 0), callback = cbcb, args = [lstlbl[0]])
|
||||||
objsched.add_thread(stop(duration, objsched)) # Commit suicide after specified no. of seconds
|
Checkbox(objsched, mytft, mytouch, (300, 50), fillcolor = RED, callback = cbcb, args = [lstlbl[1]])
|
||||||
|
|
||||||
objsched.run() # Run it!
|
objsched.run() # Run it!
|
||||||
|
|
||||||
test() # Forever: we have a Quit button!
|
test()
|
||||||
|
|
|
@ -0,0 +1,184 @@
|
||||||
|
# displays.py Non touch sensitive display elements for Pyboard TFT GUI
|
||||||
|
|
||||||
|
# The MIT License (MIT)
|
||||||
|
#
|
||||||
|
# Copyright (c) 2016 Peter Hinch
|
||||||
|
#
|
||||||
|
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
# of this software and associated documentation files (the "Software"), to deal
|
||||||
|
# in the Software without restriction, including without limitation the rights
|
||||||
|
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
# copies of the Software, and to permit persons to whom the Software is
|
||||||
|
# furnished to do so, subject to the following conditions:
|
||||||
|
#
|
||||||
|
# The above copyright notice and this permission notice shall be included in
|
||||||
|
# all copies or substantial portions of the Software.
|
||||||
|
#
|
||||||
|
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
# THE SOFTWARE.
|
||||||
|
|
||||||
|
from ui import print_centered, NoTouch, BLACK, RED
|
||||||
|
import math
|
||||||
|
import TFT_io
|
||||||
|
|
||||||
|
class Label(NoTouch):
|
||||||
|
def __init__(self, tft, location, *, font, border=None, width, fgcolor=None, bgcolor=None, fontcolor=None, text=''):
|
||||||
|
super().__init__(tft, location, font, None, width, fgcolor, bgcolor, fontcolor, border)
|
||||||
|
self.height = self.font.bits_vert
|
||||||
|
self.height += 2 * self.border # Height determined by font and border
|
||||||
|
self.draw_border() # Must explicitly draw because ctor did not have height
|
||||||
|
self.show(text)
|
||||||
|
|
||||||
|
def show(self, text):
|
||||||
|
tft = self.tft
|
||||||
|
bw = self.border
|
||||||
|
if text:
|
||||||
|
x = self.location[0]
|
||||||
|
y = self.location[1]
|
||||||
|
self.set_color(self.bgcolor)
|
||||||
|
tft.fillRectangle(x + bw, y + bw, x + self.width - bw, y + self.height - bw)
|
||||||
|
tft.setTextStyle(self.fontcolor, None, 2, self.font)
|
||||||
|
tft.setTextPos(x + bw, y + bw, clip = self.width - 2 * bw, scroll = False)
|
||||||
|
tft.printString(text)
|
||||||
|
self.restore_color() # restore fg color
|
||||||
|
|
||||||
|
# class displays angles
|
||||||
|
class Dial(NoTouch):
|
||||||
|
def __init__(self, tft, location, *, height=100, fgcolor=None, bgcolor=None, border=None, pointers=(0.9,), ticks=4):
|
||||||
|
NoTouch.__init__(self, tft, location, None, height, height, fgcolor, bgcolor, None, border) # __super__ provoked Python bug
|
||||||
|
border = self.border # border width
|
||||||
|
radius = height / 2 - border
|
||||||
|
self.radius = radius
|
||||||
|
self.xorigin = location[0] + border + radius
|
||||||
|
self.yorigin = location[1] + border + radius
|
||||||
|
self.pointers = tuple(z * self.radius for z in pointers) # Pointer lengths
|
||||||
|
self.angles = [None for _ in pointers]
|
||||||
|
self.set_color() # set fg color
|
||||||
|
ticklen = 0.1 * radius
|
||||||
|
for tick in range(ticks):
|
||||||
|
theta = 2 * tick * math.pi / ticks
|
||||||
|
x_start = int(self.xorigin + radius * math.sin(theta))
|
||||||
|
y_start = int(self.yorigin - radius * math.cos(theta))
|
||||||
|
x_end = int(self.xorigin + (radius - ticklen) * math.sin(theta))
|
||||||
|
y_end = int(self.yorigin - (radius - ticklen) * math.cos(theta))
|
||||||
|
self.tft.drawLine(x_start, y_start, x_end, y_end)
|
||||||
|
tft.drawCircle(self.xorigin, self.yorigin, radius)
|
||||||
|
self.restore_color()
|
||||||
|
|
||||||
|
def show(self, angle, pointer=0):
|
||||||
|
tft = self.tft
|
||||||
|
if self.angles[pointer] is not None:
|
||||||
|
self.set_color(self.bgcolor)
|
||||||
|
self.drawpointer(self.angles[pointer], pointer) # erase old
|
||||||
|
self.set_color()
|
||||||
|
self.drawpointer(angle, pointer) # draw new
|
||||||
|
self.angles[pointer] = angle # update old
|
||||||
|
self.restore_color()
|
||||||
|
|
||||||
|
def drawpointer(self, radians, pointer):
|
||||||
|
length = self.pointers[pointer]
|
||||||
|
x_end = int(self.xorigin + length * math.sin(radians))
|
||||||
|
y_end = int(self.yorigin - length * math.cos(radians))
|
||||||
|
self.tft.drawLine(int(self.xorigin), int(self.yorigin), x_end, y_end)
|
||||||
|
|
||||||
|
class LED(NoTouch):
|
||||||
|
def __init__(self, tft, location, *, border=None, height=30, fgcolor=None, bgcolor=None, color=RED):
|
||||||
|
super().__init__(tft, location, None, height, height, fgcolor, bgcolor, None, border)
|
||||||
|
self.radius = (self.height - 2 * self.border) / 2
|
||||||
|
self.x = location[0] + self.radius + self.border
|
||||||
|
self.y = location[1] + self.radius + self.border
|
||||||
|
self.color = color
|
||||||
|
self.off()
|
||||||
|
|
||||||
|
def _show(self, color): # Light the LED
|
||||||
|
self.set_color(color)
|
||||||
|
self.tft.fillCircle(int(self.x), int(self.y), int(self.radius))
|
||||||
|
self.set_color()
|
||||||
|
self.tft.drawCircle(int(self.x), int(self.y), int(self.radius))
|
||||||
|
self.restore_color()
|
||||||
|
|
||||||
|
def on(self, color=None): # Light in current color
|
||||||
|
if color is not None:
|
||||||
|
self.color = color
|
||||||
|
self._show(self.color)
|
||||||
|
|
||||||
|
def off(self):
|
||||||
|
self._show(BLACK)
|
||||||
|
|
||||||
|
class Meter(NoTouch):
|
||||||
|
def __init__(self, tft, location, *, font=None, height=200, width=30,
|
||||||
|
fgcolor=None, bgcolor=None, pointercolor=None, fontcolor=None,
|
||||||
|
divisions=10, legends=None, value=0):
|
||||||
|
border = 5 if font is None else 1 + font.bits_vert / 2
|
||||||
|
NoTouch.__init__(self, tft, location, font, height, width, fgcolor, bgcolor, fontcolor, border) # __super__ provoked Python bug
|
||||||
|
border = self.border # border width
|
||||||
|
self.ptrbytes = 3 * (self.width + 1) # 3 bytes per pixel
|
||||||
|
self.ptrbuf = bytearray(self.ptrbytes) #???
|
||||||
|
self.x0 = self.location[0]
|
||||||
|
self.x1 = self.location[0] + self.width
|
||||||
|
self.y0 = self.location[1] + border + 2
|
||||||
|
self.y1 = self.location[1] + self.height - border
|
||||||
|
self.divisions = divisions
|
||||||
|
self.legends = legends
|
||||||
|
self.pointercolor = pointercolor if pointercolor is not None else fgcolor
|
||||||
|
self._value = value
|
||||||
|
self._old_value = -1 # invalidate
|
||||||
|
self.ptr_y = -1 # Invalidate old position
|
||||||
|
self.show()
|
||||||
|
|
||||||
|
def show(self):
|
||||||
|
tft = self.tft
|
||||||
|
bw = self.draw_border() # and background if required. Result is width of border
|
||||||
|
width = self.width
|
||||||
|
dx = 5
|
||||||
|
self.set_color()
|
||||||
|
x0 = self.x0
|
||||||
|
x1 = self.x1
|
||||||
|
y0 = self.y0
|
||||||
|
y1 = self.y1
|
||||||
|
height = y1 - y0
|
||||||
|
if self.divisions > 0:
|
||||||
|
dy = height / (self.divisions) # Tick marks
|
||||||
|
for tick in range(self.divisions + 1):
|
||||||
|
ypos = int(y0 + dy * tick)
|
||||||
|
tft.drawHLine(x0, ypos, dx)
|
||||||
|
tft.drawHLine(x1 - dx, ypos, dx)
|
||||||
|
|
||||||
|
if self.legends is not None and self.font is not None: # Legends
|
||||||
|
tft.setTextStyle(self.fontcolor, None, 2, self.font)
|
||||||
|
if len(self.legends) <= 1:
|
||||||
|
dy = 0
|
||||||
|
else:
|
||||||
|
dy = height / (len(self.legends) -1)
|
||||||
|
yl = self.y1 # Start at bottom
|
||||||
|
for legend in self.legends:
|
||||||
|
print_centered(tft, int(self.x0 + self.width /2), int(yl), legend, self.fontcolor, self.font)
|
||||||
|
yl -= dy
|
||||||
|
|
||||||
|
y0 = self.ptr_y
|
||||||
|
y1 = y0
|
||||||
|
if self.ptr_y >= 0: # Restore background
|
||||||
|
tft.setXY(x0, y0, x1, y1)
|
||||||
|
TFT_io.tft_write_data_AS(self.ptrbuf, self.ptrbytes)
|
||||||
|
ptrpos = int(self.y1 - self._value * height)
|
||||||
|
y0 = ptrpos
|
||||||
|
y1 = ptrpos
|
||||||
|
tft.setXY(x0, y0, x1, y1) # Read background
|
||||||
|
TFT_io.tft_read_cmd_data_AS(0x2e, self.ptrbuf, self.ptrbytes)
|
||||||
|
self.ptr_y = y0
|
||||||
|
self.set_color(self.pointercolor)
|
||||||
|
tft.drawHLine(x0, y0, width) # Draw pointer
|
||||||
|
self.restore_color()
|
||||||
|
|
||||||
|
def value(self, val=None):
|
||||||
|
if val is None:
|
||||||
|
return self._value
|
||||||
|
self._value = min(max(val, 0.0), 1.0)
|
||||||
|
if self._value != self._old_value:
|
||||||
|
self._old_value = self._value
|
||||||
|
self.show()
|
|
@ -0,0 +1,124 @@
|
||||||
|
# hst.py Demo/test for Horizontal Slider class for Pyboard TFT GUI
|
||||||
|
|
||||||
|
# The MIT License (MIT)
|
||||||
|
#
|
||||||
|
# Copyright (c) 2016 Peter Hinch
|
||||||
|
#
|
||||||
|
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
# of this software and associated documentation files (the "Software"), to deal
|
||||||
|
# in the Software without restriction, including without limitation the rights
|
||||||
|
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
# copies of the Software, and to permit persons to whom the Software is
|
||||||
|
# furnished to do so, subject to the following conditions:
|
||||||
|
#
|
||||||
|
# The above copyright notice and this permission notice shall be included in
|
||||||
|
# all copies or substantial portions of the Software.
|
||||||
|
#
|
||||||
|
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
# THE SOFTWARE.
|
||||||
|
|
||||||
|
from font10 import font10
|
||||||
|
from tft import TFT, LANDSCAPE
|
||||||
|
from usched import Sched
|
||||||
|
from touch import TOUCH
|
||||||
|
from slider import HorizSlider
|
||||||
|
from button import Button
|
||||||
|
from displays import Dial, Label, LED, Meter
|
||||||
|
from ui import CLIPPED_RECT, GREEN, RED, YELLOW, WHITE, BLUE
|
||||||
|
import pyb
|
||||||
|
# CALLBACKS
|
||||||
|
# cb_end occurs when user stops touching the control
|
||||||
|
def callback(slider, args):
|
||||||
|
print('{} returned {}'.format(args[0], slider.value()))
|
||||||
|
|
||||||
|
def to_string(val):
|
||||||
|
return '{:3.1f} ohms'.format(val * 10)
|
||||||
|
|
||||||
|
def master_moved(slider, args):
|
||||||
|
val = slider.value()
|
||||||
|
slave1 = args[0]
|
||||||
|
slave1.value(val)
|
||||||
|
slave2 = args[1]
|
||||||
|
slave2.value(val)
|
||||||
|
label = args[2]
|
||||||
|
label.show(to_string(val))
|
||||||
|
led = args[3]
|
||||||
|
if val > 0.8:
|
||||||
|
led.on()
|
||||||
|
else:
|
||||||
|
led.off()
|
||||||
|
|
||||||
|
# Either slave has had its slider moved (by user or by having value altered)
|
||||||
|
def slave_moved(slider, args):
|
||||||
|
val = slider.value()
|
||||||
|
if val > 0.8:
|
||||||
|
slider.fgcolor = RED
|
||||||
|
else:
|
||||||
|
slider.fgcolor = GREEN
|
||||||
|
label = args[0]
|
||||||
|
label.show(to_string(val))
|
||||||
|
|
||||||
|
def doquit(button, args):
|
||||||
|
button.objsched.stop()
|
||||||
|
|
||||||
|
# USER TEST FUNCTION
|
||||||
|
# Common args for the labels
|
||||||
|
labels = { 'width' : 70,
|
||||||
|
'fontcolor' : WHITE,
|
||||||
|
'border' : 2,
|
||||||
|
'fgcolor' : RED,
|
||||||
|
'bgcolor' : (0, 40, 0),
|
||||||
|
}
|
||||||
|
|
||||||
|
# '0', '1','2','3','4','5','6','7','8','9','10'
|
||||||
|
# Common arguments for all three sliders
|
||||||
|
table = {'fontcolor' : WHITE,
|
||||||
|
'legends' : ('0', '5', '10'),
|
||||||
|
'cb_end' : callback,
|
||||||
|
}
|
||||||
|
# 'border' : 2,
|
||||||
|
|
||||||
|
def testmeter(meter):
|
||||||
|
oldvalue = 0
|
||||||
|
yield
|
||||||
|
while True:
|
||||||
|
val = pyb.rng()/2**30
|
||||||
|
steps = 20
|
||||||
|
delta = (val - oldvalue) / steps
|
||||||
|
for _ in range(steps):
|
||||||
|
oldvalue += delta
|
||||||
|
meter.value(oldvalue)
|
||||||
|
yield 0.05
|
||||||
|
|
||||||
|
def test(duration = 0):
|
||||||
|
print('Test TFT panel...')
|
||||||
|
objsched = Sched() # Instantiate the scheduler
|
||||||
|
mytft = TFT("SSD1963", "LB04301", LANDSCAPE)
|
||||||
|
mytouch = TOUCH("XPT2046", objsched, confidence = 50) #, calibration = (-3886,-0.1287,-3812,-0.132,-3797,-0.07685,-3798,-0.07681))
|
||||||
|
mytft.backlight(100) # light on
|
||||||
|
led = LED(mytft, (420, 0), border = 2)
|
||||||
|
meter1 = Meter(mytft, (320, 0), font=font10, legends=('0','5','10'), pointercolor = YELLOW, fgcolor = GREEN)
|
||||||
|
meter2 = Meter(mytft, (360, 0), font=font10, legends=('0','5','10'), pointercolor = YELLOW)
|
||||||
|
Button(objsched, mytft, mytouch, (420, 240), font = font10, callback = doquit, fgcolor = RED,
|
||||||
|
height = 30, text = 'Quit', shape = CLIPPED_RECT)
|
||||||
|
x = 230
|
||||||
|
lstlbl = []
|
||||||
|
for n in range(3):
|
||||||
|
lstlbl.append(Label(mytft, (x, 40 + 60 * n), font = font10, **labels))
|
||||||
|
x = 0
|
||||||
|
slave1 = HorizSlider(objsched, mytft, mytouch, (x, 100), font10,
|
||||||
|
fgcolor = GREEN, cbe_args = ('Slave1',), cb_move = slave_moved, cbm_args = (lstlbl[1],), **table)
|
||||||
|
slave2 = HorizSlider(objsched, mytft, mytouch, (x, 160), font10,
|
||||||
|
fgcolor = GREEN, cbe_args = ('Slave2',), cb_move = slave_moved, cbm_args = (lstlbl[2],), **table)
|
||||||
|
master = HorizSlider(objsched, mytft, mytouch, (x, 40), font10,
|
||||||
|
fgcolor = YELLOW, cbe_args = ('Master',), cb_move = master_moved, slidecolor=RED, cbm_args = (slave1, slave2, lstlbl[0], led), value=0.5, **table)
|
||||||
|
objsched.add_thread(testmeter(meter1))
|
||||||
|
objsched.add_thread(testmeter(meter2))
|
||||||
|
objsched.run() # Run it!
|
||||||
|
|
||||||
|
test()
|
|
@ -1,124 +1,117 @@
|
||||||
# slider.py
|
# slider.py Vertical and horizontal slider control classes for Pyboard TFT GUI
|
||||||
|
|
||||||
|
# The MIT License (MIT)
|
||||||
|
#
|
||||||
|
# Copyright (c) 2016 Peter Hinch
|
||||||
|
#
|
||||||
|
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
# of this software and associated documentation files (the "Software"), to deal
|
||||||
|
# in the Software without restriction, including without limitation the rights
|
||||||
|
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
# copies of the Software, and to permit persons to whom the Software is
|
||||||
|
# furnished to do so, subject to the following conditions:
|
||||||
|
#
|
||||||
|
# The above copyright notice and this permission notice shall be included in
|
||||||
|
# all copies or substantial portions of the Software.
|
||||||
|
#
|
||||||
|
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
# THE SOFTWARE.
|
||||||
|
|
||||||
# A slider's text items lie outside its bounding box (area sensitive to touch)
|
# A slider's text items lie outside its bounding box (area sensitive to touch)
|
||||||
|
|
||||||
from ui import touchable
|
from ui import Touchable, get_stringsize
|
||||||
from TFT_io import TFT_io
|
import TFT_io
|
||||||
class Slider(touchable):
|
|
||||||
|
class Slider(Touchable):
|
||||||
def __init__(self, objsched, tft, objtouch, location, font, *, height=200, width=30, divisions=10, legends=None,
|
def __init__(self, objsched, tft, objtouch, location, font, *, height=200, width=30, divisions=10, legends=None,
|
||||||
fgcolor=None, bgcolor=None, fontcolor=None, slidecolor=None, cb_end=lambda x, y : None,
|
fgcolor=None, bgcolor=None, fontcolor=None, slidecolor=None, border=None,
|
||||||
cbe_args=[], cb_move=lambda x, y : None, cbm_args=[], to_string=lambda x : str(x), value=0.0):
|
cb_end=lambda x, y : None, cbe_args=[], cb_move=lambda x, y : None, cbm_args=[], value=0.0):
|
||||||
super().__init__(objsched, objtouch)
|
super().__init__(objsched, tft, objtouch, location, font, height, width, fgcolor, bgcolor, fontcolor, border)
|
||||||
self.objsched = objsched
|
|
||||||
self.tft = tft
|
|
||||||
self.location = location
|
|
||||||
self.height = height
|
|
||||||
self.width = width
|
|
||||||
self.divisions = divisions
|
self.divisions = divisions
|
||||||
self.legends = legends
|
self.legends = legends
|
||||||
self.fgcolor = fgcolor
|
|
||||||
self.bgcolor = bgcolor
|
|
||||||
self.fontcolor = fontcolor if fontcolor is not None else tft.getColor()
|
|
||||||
self.font = font
|
|
||||||
self.slidecolor = slidecolor
|
self.slidecolor = slidecolor
|
||||||
self.cb_end = cb_end
|
self.cb_end = cb_end
|
||||||
self.cbe_args = cbe_args
|
self.cbe_args = cbe_args
|
||||||
self.cb_move = cb_move
|
self.cb_move = cb_move
|
||||||
self.cbm_args = cbm_args
|
self.cbm_args = cbm_args
|
||||||
self.to_string = to_string # Applied to display at bottom: user converts 0-1.0 to string with any scaling applied
|
|
||||||
self.was_touched = False
|
self.was_touched = False
|
||||||
self.old_text_end = False
|
slidewidth = int(width / 1.3) & 0xfe # Ensure divisible by 2
|
||||||
self.slidewidth = int(width / 1.3)
|
|
||||||
self.slidewidth += self.slidewidth % 2 # Ensure divisible by 2
|
|
||||||
self.slideheight = 6 # must be divisible by 2
|
self.slideheight = 6 # must be divisible by 2
|
||||||
# We draw an odd number of pixels:
|
# We draw an odd number of pixels:
|
||||||
self.slidebytes = (self.slideheight + 1) * (self.slidewidth + 1) * 3
|
self.slidebytes = (self.slideheight + 1) * (slidewidth + 1) * 3
|
||||||
self.slidebuf = bytearray(self.slidebytes)
|
self.slidebuf = bytearray(self.slidebytes)
|
||||||
self.slide_x = -1
|
self._old_value = -1 # Invalidate
|
||||||
|
b = self.border
|
||||||
|
self.pot_dimension = self.height - 2 * (b + self.slideheight // 2)
|
||||||
|
width = self.width - 2 * b
|
||||||
|
xcentre = self.location[0] + b + width // 2
|
||||||
|
self.slide_x0 = xcentre - slidewidth // 2
|
||||||
|
self.slide_x1 = xcentre + slidewidth // 2 # slide X coordinates
|
||||||
self.slide_y = -1 # Invalidate old position
|
self.slide_y = -1 # Invalidate old position
|
||||||
self.border_y = min(self.slideheight // 2, 10) # Allow space above and below slot
|
self.value(value)
|
||||||
|
|
||||||
self._value = min(max(value, 0.0), 1.0) # User supplies 0-1.0
|
|
||||||
self.show()
|
|
||||||
objsched.add_thread(self.mainthread())
|
|
||||||
|
|
||||||
def show(self):
|
def show(self):
|
||||||
tft = self.tft
|
tft = self.tft
|
||||||
fgcolor = tft.getColor() # save old colors
|
bw = self.draw_border() # and background if required. Result is width of border
|
||||||
bgcolor = tft.getBGColor()
|
x = self.location[0] + bw
|
||||||
mybgcolor = bgcolor
|
y = self.location[1] + bw + self.slideheight // 2 # Allow space above and below slot
|
||||||
x = self.location[0]
|
width = self.width - 2 * bw
|
||||||
y = self.location[1] + self.border_y
|
self.set_color()
|
||||||
if self.bgcolor is not None:
|
height = self.pot_dimension # Height of slot
|
||||||
tft.setColor(self.bgcolor)
|
dx = width / 3
|
||||||
else:
|
|
||||||
tft.setColor(bgcolor)
|
|
||||||
tft.setTextStyle(self.fontcolor, None, 2, self.font)
|
|
||||||
if self.fgcolor is not None:
|
|
||||||
tft.setColor(self.fgcolor)
|
|
||||||
else:
|
|
||||||
tft.setColor(fgcolor)
|
|
||||||
if self.bgcolor is not None:
|
|
||||||
mybgcolor = self.bgcolor
|
|
||||||
tft.setBGColor(mybgcolor)
|
|
||||||
height = self.height
|
|
||||||
width = self.width
|
|
||||||
dx = width // 3
|
|
||||||
xcentre = x + width // 2
|
|
||||||
tft.drawRectangle(x + dx, y, x + 2 * dx, y + height)
|
tft.drawRectangle(x + dx, y, x + 2 * dx, y + height)
|
||||||
|
|
||||||
if self.divisions > 0:
|
if self.divisions > 0:
|
||||||
dy = height // self.divisions # Tick marks
|
dy = height / (self.divisions) # Tick marks
|
||||||
ytick = y
|
|
||||||
fhdelta = self.font.bits_vert // 2
|
|
||||||
for tick in range(self.divisions + 1):
|
for tick in range(self.divisions + 1):
|
||||||
tft.drawHLine(x, ytick, dx)
|
ypos = int(y + dy * tick)
|
||||||
tft.drawHLine(x + 2 * dx, ytick, dx)
|
tft.drawHLine(x + 1, ypos, dx)
|
||||||
ytick += dy
|
tft.drawHLine(x + 1 + 2 * dx, ypos, dx)
|
||||||
|
|
||||||
if self.legends is not None: # Legends
|
if self.legends is not None: # Legends
|
||||||
|
tft.setTextStyle(self.fontcolor, None, 2, self.font)
|
||||||
if len(self.legends) <= 1:
|
if len(self.legends) <= 1:
|
||||||
dy = 0
|
dy = 0
|
||||||
else:
|
else:
|
||||||
dy = height // (len(self.legends) -1)
|
dy = height / (len(self.legends) -1)
|
||||||
yl = y + height # Start at bottom
|
yl = y + height # Start at bottom
|
||||||
|
fhdelta = self.font.bits_vert / 2
|
||||||
for legend in self.legends:
|
for legend in self.legends:
|
||||||
tft.setTextPos(x + width, yl - fhdelta)
|
tft.setTextPos(x + self.width, int(yl - fhdelta))
|
||||||
tft.printString(legend)
|
tft.printString(legend)
|
||||||
yl -= dy
|
yl -= dy
|
||||||
|
|
||||||
sw = self.slidewidth # Handle slider
|
sh = self.slideheight # Handle slider
|
||||||
sh = self.slideheight
|
x0 = self.slide_x0
|
||||||
sliderpos = int(y + height - self._value * height)
|
y0 = self.slide_y
|
||||||
if self.slidecolor is not None:
|
x1 = self.slide_x1
|
||||||
tft.setColor(self.slidecolor)
|
y1 = y0 + sh
|
||||||
if self.slide_x >= 0: # Restore background
|
if self.slide_y >= 0: # Restore background
|
||||||
tft.setXY(self.slide_x, self.slide_y, self.slide_x + sw, self.slide_y + sh)
|
tft.setXY(x0, y0, x1, y1)
|
||||||
TFT_io.tft_write_data_AS(self.slidebuf, self.slidebytes)
|
TFT_io.tft_write_data_AS(self.slidebuf, self.slidebytes)
|
||||||
x0 = xcentre - sw // 2
|
sliderpos = int(y + height - self._value * height)
|
||||||
y0 = sliderpos - sh // 2
|
y0 = sliderpos - sh // 2
|
||||||
x1 = xcentre + sw // 2
|
|
||||||
y1 = sliderpos + sh // 2
|
y1 = sliderpos + sh // 2
|
||||||
tft.setXY(x0, y0, x1, y1) # Read background
|
tft.setXY(x0, y0, x1, y1) # Read background
|
||||||
TFT_io.tft_read_cmd_data_AS(0x2e, self.slidebuf, self.slidebytes)
|
TFT_io.tft_read_cmd_data_AS(0x2e, self.slidebuf, self.slidebytes)
|
||||||
self.slide_x = x0
|
|
||||||
self.slide_y = y0
|
self.slide_y = y0
|
||||||
|
if self.slidecolor is not None:
|
||||||
|
self.set_color(self.slidecolor)
|
||||||
tft.fillRectangle(x0, y0, x1, y1) # Draw slider
|
tft.fillRectangle(x0, y0, x1, y1) # Draw slider
|
||||||
|
self.restore_color()
|
||||||
textx = x
|
|
||||||
texty = y + height + 2 * self.border_y
|
|
||||||
tft.setTextPos(textx, texty)
|
|
||||||
if self.old_text_end:
|
|
||||||
tft.setColor(mybgcolor)
|
|
||||||
tft.fillRectangle(textx, texty, self.old_text_end, texty + self.font.bits_vert)
|
|
||||||
tft.printString(self.to_string(self._value))
|
|
||||||
self.old_text_end = tft.getTextPos()[0]
|
|
||||||
tft.setColor(fgcolor) # restore them
|
|
||||||
tft.setBGColor(bgcolor)
|
|
||||||
|
|
||||||
def value(self, val=None):
|
def value(self, val=None):
|
||||||
if val is None:
|
if val is None:
|
||||||
return self._value
|
return self._value
|
||||||
self._value = min(max(val, 0.0), 1.0)
|
self._value = min(max(val, 0.0), 1.0)
|
||||||
|
if self._value != self._old_value:
|
||||||
|
self._old_value = self._value
|
||||||
|
self.cb_move(self, self.cbm_args) # Callback not a bound method so pass self
|
||||||
self.show()
|
self.show()
|
||||||
|
|
||||||
def touched(self, x, y): # If touched in bounding box, process it otherwise do nothing
|
def touched(self, x, y): # If touched in bounding box, process it otherwise do nothing
|
||||||
|
@ -128,19 +121,111 @@ class Slider(touchable):
|
||||||
y1 = self.location[1] + self.height
|
y1 = self.location[1] + self.height
|
||||||
if x0 <= x <= x1 and y0 <= y <= y1:
|
if x0 <= x <= x1 and y0 <= y <= y1:
|
||||||
self.was_touched = True
|
self.was_touched = True
|
||||||
self._value = (y1 - y) / self.height
|
self.value((y1 - y) / self.pot_dimension)
|
||||||
|
|
||||||
def untouched(self): # User has released touchpad or touched elsewhere
|
def untouched(self): # User has released touchpad or touched elsewhere
|
||||||
if self.was_touched:
|
if self.was_touched:
|
||||||
self.cb_end(self, self.cbe_args) # Callback not a bound method so pass self
|
self.cb_end(self, self.cbe_args) # Callback not a bound method so pass self
|
||||||
self.was_touched = False
|
self.was_touched = False
|
||||||
|
|
||||||
def mainthread(self):
|
class HorizSlider(Touchable):
|
||||||
old_value = self._value
|
def __init__(self, objsched, tft, objtouch, location, font, *, height=30, width=200, divisions=10, legends=None,
|
||||||
while True:
|
fgcolor=None, bgcolor=None, fontcolor=None, slidecolor=None, border=None,
|
||||||
yield
|
cb_end=lambda x, y : None, cbe_args=[], cb_move=lambda x, y : None, cbm_args=[], value=0.0):
|
||||||
val = self._value
|
super().__init__(objsched, tft, objtouch, location, font, height, width, fgcolor, bgcolor, fontcolor, border)
|
||||||
if val != old_value:
|
self.divisions = divisions
|
||||||
old_value = val
|
self.legends = legends
|
||||||
|
self.slidecolor = slidecolor
|
||||||
|
self.cb_end = cb_end
|
||||||
|
self.cbe_args = cbe_args
|
||||||
|
self.cb_move = cb_move
|
||||||
|
self.cbm_args = cbm_args
|
||||||
|
self.was_touched = False
|
||||||
|
slideheight = int(height / 1.3) & 0xfe # Ensure divisible by 2
|
||||||
|
self.slidewidth = 6 # must be divisible by 2
|
||||||
|
# We draw an odd number of pixels:
|
||||||
|
self.slidebytes = (slideheight + 1) * (self.slidewidth + 1) * 3
|
||||||
|
self.slidebuf = bytearray(self.slidebytes)
|
||||||
|
self._old_value = -1 # Invalidate
|
||||||
|
b = self.border
|
||||||
|
self.pot_dimension = self.width - 2 * (b + self.slidewidth // 2)
|
||||||
|
height = self.height - 2 * b
|
||||||
|
ycentre = self.location[1] + b + height // 2
|
||||||
|
self.slide_y0 = ycentre - slideheight // 2
|
||||||
|
self.slide_y1 = ycentre + slideheight // 2 # slide Y coordinates
|
||||||
|
self.slide_x = -1 # Invalidate old position
|
||||||
|
self.value(value)
|
||||||
|
|
||||||
|
def show(self):
|
||||||
|
tft = self.tft
|
||||||
|
bw = self.draw_border() # and background if required. Result is width of border
|
||||||
|
x = self.location[0] + bw + self.slidewidth // 2 # Allow space left and right slot for slider at extremes
|
||||||
|
y = self.location[1] + bw
|
||||||
|
height = self.height - 2 * bw
|
||||||
|
self.set_color()
|
||||||
|
width = self.pot_dimension # Length of slot
|
||||||
|
dy = height / 3
|
||||||
|
ycentre = y + height // 2
|
||||||
|
tft.drawRectangle(x, y + dy, x + width, y + 2 * dy)
|
||||||
|
|
||||||
|
if self.divisions > 0:
|
||||||
|
dx = width / (self.divisions) # Tick marks
|
||||||
|
for tick in range(self.divisions + 1):
|
||||||
|
xpos = int(x + dx * tick)
|
||||||
|
tft.drawVLine(xpos, y + 1, dy) # TODO Why is +1 fiddle required here?
|
||||||
|
tft.drawVLine(xpos, y + 1 + 2 * dy, dy) # and here
|
||||||
|
|
||||||
|
if self.legends is not None: # Legends
|
||||||
|
tft.setTextStyle(self.fontcolor, None, 2, self.font)
|
||||||
|
if len(self.legends) <= 1:
|
||||||
|
dx = 0
|
||||||
|
else:
|
||||||
|
dx = width / (len(self.legends) -1)
|
||||||
|
xl = x
|
||||||
|
for legend in self.legends:
|
||||||
|
offset = get_stringsize(legend, self.font)[0] / 2
|
||||||
|
tft.setTextPos(int(xl - offset), y - self.font.bits_vert) # Arbitrary left shift should be char width /2
|
||||||
|
tft.printString(legend)
|
||||||
|
xl += dx
|
||||||
|
|
||||||
|
sw = self.slidewidth # Handle slider
|
||||||
|
x0 = self.slide_x
|
||||||
|
y0 = self.slide_y0
|
||||||
|
x1 = x0 + sw
|
||||||
|
y1 = self.slide_y1
|
||||||
|
if self.slide_x >= 0: # Restore background
|
||||||
|
tft.setXY(x0, y0, x1, y1)
|
||||||
|
TFT_io.tft_write_data_AS(self.slidebuf, self.slidebytes)
|
||||||
|
sliderpos = int(x + self._value * width)
|
||||||
|
x0 = sliderpos - sw // 2
|
||||||
|
x1 = sliderpos + sw // 2
|
||||||
|
tft.setXY(x0, y0, x1, y1) # Read background
|
||||||
|
TFT_io.tft_read_cmd_data_AS(0x2e, self.slidebuf, self.slidebytes)
|
||||||
|
self.slide_x = x0
|
||||||
|
if self.slidecolor is not None:
|
||||||
|
self.set_color(self.slidecolor)
|
||||||
|
tft.fillRectangle(x0, y0, x1, y1) # Draw slider
|
||||||
|
self.restore_color()
|
||||||
|
|
||||||
|
def value(self, val=None):
|
||||||
|
if val is None:
|
||||||
|
return self._value
|
||||||
|
self._value = min(max(val, 0.0), 1.0)
|
||||||
|
if self._value != self._old_value:
|
||||||
|
self._old_value = self._value
|
||||||
self.cb_move(self, self.cbm_args) # Callback not a bound method so pass self
|
self.cb_move(self, self.cbm_args) # Callback not a bound method so pass self
|
||||||
self.show()
|
self.show()
|
||||||
|
|
||||||
|
def touched(self, x, y): # If touched in bounding box, process it otherwise do nothing
|
||||||
|
x0 = self.location[0]
|
||||||
|
x1 = self.location[0] + self.width
|
||||||
|
y0 = self.location[1]
|
||||||
|
y1 = self.location[1] + self.height
|
||||||
|
if x0 <= x <= x1 and y0 <= y <= y1:
|
||||||
|
self.was_touched = True
|
||||||
|
self.value((x - x0) / self.pot_dimension)
|
||||||
|
|
||||||
|
def untouched(self): # User has released touchpad or touched elsewhere
|
||||||
|
if self.was_touched:
|
||||||
|
self.cb_end(self, self.cbe_args) # Callback not a bound method so pass self
|
||||||
|
self.was_touched = False
|
||||||
|
|
|
@ -1,52 +1,36 @@
|
||||||
|
# slidetest.py Demo/test program for vertical slider class for Pyboard TFT GUI
|
||||||
|
|
||||||
|
# The MIT License (MIT)
|
||||||
|
#
|
||||||
|
# Copyright (c) 2016 Peter Hinch
|
||||||
|
#
|
||||||
|
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
# of this software and associated documentation files (the "Software"), to deal
|
||||||
|
# in the Software without restriction, including without limitation the rights
|
||||||
|
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
# copies of the Software, and to permit persons to whom the Software is
|
||||||
|
# furnished to do so, subject to the following conditions:
|
||||||
|
#
|
||||||
|
# The above copyright notice and this permission notice shall be included in
|
||||||
|
# all copies or substantial portions of the Software.
|
||||||
|
#
|
||||||
|
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
# THE SOFTWARE.
|
||||||
|
|
||||||
import gc
|
|
||||||
from font10 import font10
|
from font10 import font10
|
||||||
from tft import TFT, LANDSCAPE
|
from tft import TFT, LANDSCAPE
|
||||||
from usched import Sched
|
from usched import Sched
|
||||||
from touch import TOUCH
|
from touch import TOUCH
|
||||||
from slider import Slider
|
from slider import Slider
|
||||||
from button import Button
|
from button import Button
|
||||||
from ui import CLIPPED_RECT
|
from displays import Dial, Label
|
||||||
import math
|
from ui import CLIPPED_RECT, WHITE, BLACK, RED, GREEN, BLUE, YELLOW, GREY
|
||||||
#gc.collect()
|
from math import pi
|
||||||
|
|
||||||
class Dial(object):
|
|
||||||
def __init__(self, objsched, tft, x, y, r, l):
|
|
||||||
self.objsched = objsched
|
|
||||||
self.tft = tft
|
|
||||||
self.xorigin = x
|
|
||||||
self.yorigin = y
|
|
||||||
self.radius = r
|
|
||||||
self.pointerlen = l
|
|
||||||
self.angle = None
|
|
||||||
self.delta = 1
|
|
||||||
tft.drawCircle(x, y, r)
|
|
||||||
objsched.add_thread(self.mainthread())
|
|
||||||
|
|
||||||
def update(self, angle):
|
|
||||||
tft = self.tft
|
|
||||||
fgcolor = tft.getColor() # save old colors
|
|
||||||
bgcolor = tft.getBGColor()
|
|
||||||
if self.angle is not None:
|
|
||||||
tft.setColor(bgcolor)
|
|
||||||
self.drawpointer(self.angle) # erase old
|
|
||||||
tft.setColor(fgcolor)
|
|
||||||
self.drawpointer(angle) # draw new
|
|
||||||
self.angle = angle # update old
|
|
||||||
tft.setColor(fgcolor) # restore them
|
|
||||||
tft.setBGColor(bgcolor)
|
|
||||||
|
|
||||||
def drawpointer(self, radians):
|
|
||||||
x_end = int(self.xorigin + self.pointerlen * math.sin(radians))
|
|
||||||
y_end = int(self.yorigin - self.pointerlen * math.cos(radians))
|
|
||||||
self.tft.drawLine(self.xorigin, self.yorigin, x_end, y_end)
|
|
||||||
|
|
||||||
def mainthread(self):
|
|
||||||
while True:
|
|
||||||
yield 0.1
|
|
||||||
angle = self.angle if self.angle is not None else 0
|
|
||||||
angle += math.pi * 2 * self.delta / 10
|
|
||||||
self.update(angle)
|
|
||||||
|
|
||||||
# CALLBACKS
|
# CALLBACKS
|
||||||
# cb_end occurs when user stops touching the control
|
# cb_end occurs when user stops touching the control
|
||||||
|
@ -54,7 +38,7 @@ def callback(slider, args):
|
||||||
print('{} returned {}'.format(args[0], slider.value()))
|
print('{} returned {}'.format(args[0], slider.value()))
|
||||||
|
|
||||||
def to_string(val):
|
def to_string(val):
|
||||||
return '{:4.1f}ohms'.format(val * 10)
|
return '{:3.1f} ohms'.format(val * 10)
|
||||||
|
|
||||||
def master_moved(slider, args):
|
def master_moved(slider, args):
|
||||||
val = slider.value()
|
val = slider.value()
|
||||||
|
@ -62,41 +46,70 @@ def master_moved(slider, args):
|
||||||
slave1.value(val)
|
slave1.value(val)
|
||||||
slave2 = args[1]
|
slave2 = args[1]
|
||||||
slave2.value(val)
|
slave2.value(val)
|
||||||
|
label = args[2]
|
||||||
|
label.show(to_string(val))
|
||||||
|
|
||||||
# Either slave has had its slider moved (by user or by having value altered)
|
# Either slave has had its slider moved (by user or by having value altered)
|
||||||
def slave_moved(slider, args):
|
def slave_moved(slider, args):
|
||||||
|
val = slider.value()
|
||||||
dial = args[0]
|
dial = args[0]
|
||||||
dial.delta = slider.value()
|
dial.delta = val
|
||||||
|
label = args[1]
|
||||||
|
label.show(to_string(val))
|
||||||
|
|
||||||
def doquit(button, args):
|
def doquit(button, args):
|
||||||
button.objsched.stop()
|
button.objsched.stop()
|
||||||
|
|
||||||
# USER TEST FUNCTION
|
# THREADS
|
||||||
|
def mainthread(slider, dial):
|
||||||
|
angle = 0
|
||||||
|
yield
|
||||||
|
while True:
|
||||||
|
yield 0.1
|
||||||
|
delta = slider.value()
|
||||||
|
angle += pi * 2 * delta / 10
|
||||||
|
dial.show(angle)
|
||||||
|
dial.show(angle /10, 1)
|
||||||
|
|
||||||
|
# DATA
|
||||||
|
# Common args for the labels
|
||||||
|
labels = { 'width' : 70,
|
||||||
|
'fontcolor' : WHITE,
|
||||||
|
'border' : 2,
|
||||||
|
'fgcolor' : RED,
|
||||||
|
'bgcolor' : (0, 40, 0),
|
||||||
|
}
|
||||||
|
|
||||||
# '0', '1','2','3','4','5','6','7','8','9','10'
|
# '0', '1','2','3','4','5','6','7','8','9','10'
|
||||||
# Common arguments for all three sliders
|
# Common arguments for all three sliders
|
||||||
table = {'fontcolor' : (255, 255, 255),
|
table = {'fontcolor' : WHITE,
|
||||||
'legends' : ('0', '5', '10'),
|
'legends' : ('0', '5', '10'),
|
||||||
'to_string' : to_string,
|
|
||||||
'cb_end' : callback,
|
'cb_end' : callback,
|
||||||
'value' : 0.5}
|
}
|
||||||
|
# 'border' : 2,
|
||||||
|
|
||||||
def test(duration = 0):
|
def test(duration = 0):
|
||||||
print('Test TFT panel...')
|
print('Test TFT panel...')
|
||||||
objsched = Sched() # Instantiate the scheduler
|
objsched = Sched() # Instantiate the scheduler
|
||||||
mytft = TFT("SSD1963", "LB04301", LANDSCAPE)
|
mytft = TFT("SSD1963", "LB04301", LANDSCAPE)
|
||||||
mytouch = TOUCH("XPT2046", objsched)
|
mytouch = TOUCH("XPT2046", objsched, confidence=50)
|
||||||
mytft.backlight(100) # light on
|
mytft.backlight(100) # light on
|
||||||
Button(objsched, mytft, mytouch, (400, 240), font = font10, callback = doquit, fgcolor = (255, 0, 0),
|
Button(objsched, mytft, mytouch, (400, 240), font = font10, callback = doquit, fgcolor = RED,
|
||||||
height = 30, text = 'Quit', shape = CLIPPED_RECT)
|
height = 30, text = 'Quit', shape = CLIPPED_RECT)
|
||||||
dial1 = Dial(objsched, mytft, 350, 60, 50, 48)
|
dial1 = Dial(mytft, (350, 10), fgcolor = YELLOW, border = 2, pointers = (0.9, 0.7))
|
||||||
dial2 = Dial(objsched, mytft, 350, 170, 50, 48)
|
dial2 = Dial(mytft, (350, 120), fgcolor = YELLOW, bgcolor = GREY, border = 2, pointers = (0.9, 0.7))
|
||||||
|
lstlbl = []
|
||||||
|
for n in range(3):
|
||||||
|
lstlbl.append(Label(mytft, (80 * n, 240), font = font10, **labels))
|
||||||
y = 5
|
y = 5
|
||||||
slave1 = Slider(objsched, mytft, mytouch, (80, y), font10,
|
slave1 = Slider(objsched, mytft, mytouch, (80, y), font10,
|
||||||
fgcolor = (0, 255, 0), cbe_args = ('Slave1',), cb_move = slave_moved, cbm_args = (dial1,), **table)
|
fgcolor = (0, 255, 0), cbe_args = ('Slave1',), cb_move = slave_moved, cbm_args = (dial1, lstlbl[1]), **table)
|
||||||
slave2 = Slider(objsched, mytft, mytouch, (160, y), font10,
|
slave2 = Slider(objsched, mytft, mytouch, (160, y), font10,
|
||||||
fgcolor = (0, 255, 0), cbe_args = ('Slave2',), cb_move = slave_moved, cbm_args = (dial2,), **table)
|
fgcolor = (0, 255, 0), cbe_args = ('Slave2',), cb_move = slave_moved, cbm_args = (dial2, lstlbl[2]), **table)
|
||||||
Slider(objsched, mytft, mytouch, (0, y), font10,
|
master = Slider(objsched, mytft, mytouch, (0, y), font10,
|
||||||
fgcolor = (255, 255, 0), cbe_args = ('Master',), cb_move = master_moved, cbm_args = (slave1, slave2), **table)
|
fgcolor = (255, 255, 0), cbe_args = ('Master',), cb_move = master_moved, cbm_args = (slave1, slave2, lstlbl[0]), value=0.5, **table)
|
||||||
|
objsched.add_thread(mainthread(slave1, dial1))
|
||||||
|
objsched.add_thread(mainthread(slave2, dial2))
|
||||||
objsched.run() # Run it!
|
objsched.run() # Run it!
|
||||||
|
|
||||||
test()
|
test()
|
||||||
|
|
|
@ -1,8 +1,37 @@
|
||||||
# ui.py Base classes and utilities for TFT GUI
|
# ui.py Constants, base classes and utilities for Pybboard TFT GUI
|
||||||
|
|
||||||
|
# The MIT License (MIT)
|
||||||
|
#
|
||||||
|
# Copyright (c) 2016 Peter Hinch
|
||||||
|
#
|
||||||
|
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
# of this software and associated documentation files (the "Software"), to deal
|
||||||
|
# in the Software without restriction, including without limitation the rights
|
||||||
|
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
# copies of the Software, and to permit persons to whom the Software is
|
||||||
|
# furnished to do so, subject to the following conditions:
|
||||||
|
#
|
||||||
|
# The above copyright notice and this permission notice shall be included in
|
||||||
|
# all copies or substantial portions of the Software.
|
||||||
|
#
|
||||||
|
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
# THE SOFTWARE.
|
||||||
|
|
||||||
CIRCLE = 1
|
CIRCLE = 1
|
||||||
RECTANGLE = 2
|
RECTANGLE = 2
|
||||||
CLIPPED_RECT = 3
|
CLIPPED_RECT = 3
|
||||||
|
WHITE = (255, 255, 255)
|
||||||
|
BLACK = (0, 0, 0)
|
||||||
|
RED = (255, 0, 0)
|
||||||
|
GREEN = (0, 255, 0)
|
||||||
|
BLUE = (0, 0, 255)
|
||||||
|
YELLOW = (255, 255, 0)
|
||||||
|
GREY = (100, 100, 100)
|
||||||
|
|
||||||
def get_stringsize(s, font):
|
def get_stringsize(s, font):
|
||||||
hor = 0
|
hor = 0
|
||||||
|
@ -17,8 +46,51 @@ def print_centered(tft, x, y, s, color, font):
|
||||||
tft.setTextPos(x - length // 2, y - height // 2)
|
tft.setTextPos(x - length // 2, y - height // 2)
|
||||||
tft.printString(s)
|
tft.printString(s)
|
||||||
|
|
||||||
|
# Base class for all displayable objects
|
||||||
|
class NoTouch(object):
|
||||||
|
old_color = None
|
||||||
|
def __init__(self, tft, location, font, height, width, fgcolor, bgcolor, fontcolor, border):
|
||||||
|
self.tft = tft
|
||||||
|
self.location = location
|
||||||
|
self.font = font
|
||||||
|
self.height = height
|
||||||
|
self.width = width
|
||||||
|
self.fill = bgcolor is not None
|
||||||
|
self.fgcolor = fgcolor if fgcolor is not None else tft.getColor()
|
||||||
|
self.bgcolor = bgcolor if bgcolor is not None else tft.getBGColor()
|
||||||
|
self.fontcolor = fontcolor if fontcolor is not None else tft.getColor()
|
||||||
|
self.hasborder = border is not None
|
||||||
|
self.border = 0 if border is None else border
|
||||||
|
if NoTouch.old_color is None:
|
||||||
|
NoTouch.old_color = tft.getColor()
|
||||||
|
if height is not None and width is not None: # beware special cases where height and width not yet known
|
||||||
|
self.draw_border()
|
||||||
|
|
||||||
|
def draw_border(self): # Draw background and bounding box if required
|
||||||
|
tft = self.tft
|
||||||
|
fgcolor = tft.getColor()
|
||||||
|
x = self.location[0]
|
||||||
|
y = self.location[1]
|
||||||
|
if self.fill:
|
||||||
|
tft.setColor(self.bgcolor)
|
||||||
|
tft.fillRectangle(x, y, x + self.width, y + self.height)
|
||||||
|
bw = 0 # border width
|
||||||
|
if self.hasborder: # Draw a bounding box
|
||||||
|
bw = self.border
|
||||||
|
tft.setColor(self.fgcolor)
|
||||||
|
tft.drawRectangle(x, y, x + self.width, y + self.height)
|
||||||
|
tft.setColor(fgcolor)
|
||||||
|
return bw # Actual width (may be 0)
|
||||||
|
|
||||||
|
def set_color(self, color=None):
|
||||||
|
new = self.fgcolor if color is None else color
|
||||||
|
self.tft.setColor(new)
|
||||||
|
|
||||||
|
def restore_color(self): # Restore to system default
|
||||||
|
self.tft.setColor(NoTouch.old_color)
|
||||||
|
|
||||||
# Base class for touch-enabled classes.
|
# Base class for touch-enabled classes.
|
||||||
class touchable(object):
|
class Touchable(NoTouch):
|
||||||
touchlist = []
|
touchlist = []
|
||||||
objtouch = None
|
objtouch = None
|
||||||
|
|
||||||
|
@ -36,9 +108,11 @@ class touchable(object):
|
||||||
for obj in cls.touchlist:
|
for obj in cls.touchlist:
|
||||||
obj.untouched()
|
obj.untouched()
|
||||||
|
|
||||||
def __init__(self, objsched, objtouch):
|
def __init__(self, objsched, tft, objtouch, location, font, height, width, fgcolor, bgcolor, fontcolor, border):
|
||||||
touchable.touchlist.append(self)
|
super().__init__(tft, location, font, height, width, fgcolor, bgcolor, fontcolor, border)
|
||||||
|
Touchable.touchlist.append(self)
|
||||||
self.enabled = True # Available to user/subclass
|
self.enabled = True # Available to user/subclass
|
||||||
if touchable.objtouch is None: # Initialising class and thread
|
self.objsched = objsched
|
||||||
touchable.objtouch = objtouch
|
if Touchable.objtouch is None: # Initialising class and thread
|
||||||
|
Touchable.objtouch = objtouch
|
||||||
objsched.add_thread(self.touchtest()) # One thread only
|
objsched.add_thread(self.touchtest()) # One thread only
|
||||||
|
|
Ładowanie…
Reference in New Issue