Merge 21ce0d8ae1 into 1daa2d72aa
This commit is contained in:
commit
97a64b8267
3 changed files with 389 additions and 10 deletions
|
|
@ -16,12 +16,26 @@ Licensed under the MIT license.
|
||||||
All text above must be included in any redistribution.
|
All text above must be included in any redistribution.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
import array
|
||||||
import gc
|
import gc
|
||||||
import time
|
import time
|
||||||
|
import os
|
||||||
|
import supervisor
|
||||||
import board
|
import board
|
||||||
import displayio
|
import displayio
|
||||||
import adafruit_logging as logging
|
import adafruit_logging as logging
|
||||||
|
|
||||||
|
try:
|
||||||
|
import usb.core
|
||||||
|
usb_available = True
|
||||||
|
except ImportError:
|
||||||
|
usb_available = False
|
||||||
|
|
||||||
|
try:
|
||||||
|
from adafruit_fruitjam.peripherals import request_display_config
|
||||||
|
from adafruit_fruitjam.peripherals import VALID_DISPLAY_SIZES
|
||||||
|
except ImportError:
|
||||||
|
request_display_config = None
|
||||||
try:
|
try:
|
||||||
import adafruit_touchscreen
|
import adafruit_touchscreen
|
||||||
except ImportError:
|
except ImportError:
|
||||||
|
|
@ -91,9 +105,9 @@ class TouchscreenPoller(object):
|
||||||
if p is not None:
|
if p is not None:
|
||||||
self._cursor_grp.x = p[0] - self._x_offset
|
self._cursor_grp.x = p[0] - self._x_offset
|
||||||
self._cursor_grp.y = p[1] - self._y_offset
|
self._cursor_grp.y = p[1] - self._y_offset
|
||||||
return True, p
|
return True, False, p
|
||||||
else:
|
else:
|
||||||
return False, None
|
return False, False, None
|
||||||
|
|
||||||
def poke(self, location=None):
|
def poke(self, location=None):
|
||||||
"""Force a bitmap refresh."""
|
"""Force a bitmap refresh."""
|
||||||
|
|
@ -143,7 +157,7 @@ class CursorPoller(object):
|
||||||
self._mouse_cursor.x + self._x_offset,
|
self._mouse_cursor.x + self._x_offset,
|
||||||
self._mouse_cursor.y + self._y_offset,
|
self._mouse_cursor.y + self._y_offset,
|
||||||
)
|
)
|
||||||
return a_button, location
|
return a_button, False, location
|
||||||
|
|
||||||
def poke(self, x=None, y=None):
|
def poke(self, x=None, y=None):
|
||||||
"""Force a bitmap refresh."""
|
"""Force a bitmap refresh."""
|
||||||
|
|
@ -161,9 +175,281 @@ class CursorPoller(object):
|
||||||
|
|
||||||
################################################################################
|
################################################################################
|
||||||
|
|
||||||
|
class MousePoller(object):
|
||||||
|
"""Get 'pressed' and location updates from a USB mouse."""
|
||||||
|
|
||||||
|
def __init__(self, splash, cursor_bmp, screen_width, screen_height):
|
||||||
|
self._logger = logging.getLogger("Paint")
|
||||||
|
if not self._logger.hasHandlers():
|
||||||
|
self._logger.addHandler(logging.StreamHandler())
|
||||||
|
self._logger.debug("Creating a MousePoller")
|
||||||
|
self._display_grp = splash
|
||||||
|
self._cursor_grp = displayio.Group()
|
||||||
|
self._cur_palette = displayio.Palette(3)
|
||||||
|
self._cur_palette.make_transparent(0)
|
||||||
|
self._cur_palette[1] = 0xFFFFFF
|
||||||
|
self._cur_palette[2] = 0x0000
|
||||||
|
self._cur_sprite = displayio.TileGrid(
|
||||||
|
cursor_bmp, pixel_shader=self._cur_palette
|
||||||
|
)
|
||||||
|
self._cursor_grp.append(self._cur_sprite)
|
||||||
|
self._display_grp.append(self._cursor_grp)
|
||||||
|
self._x_offset = cursor_bmp.width // 2
|
||||||
|
self._y_offset = cursor_bmp.height // 2
|
||||||
|
|
||||||
|
self.SCREEN_WIDTH = screen_width
|
||||||
|
self.SCREEN_HEIGHT = screen_height
|
||||||
|
|
||||||
|
# Mouse state
|
||||||
|
self.last_left_button_state = 0
|
||||||
|
self.last_right_button_state = 0
|
||||||
|
self.left_button_pressed = False
|
||||||
|
self.right_button_pressed = False
|
||||||
|
self.mouse = None
|
||||||
|
self.buf = None
|
||||||
|
self.in_endpoint = None
|
||||||
|
|
||||||
|
# Mouse position
|
||||||
|
self.mouse_x = screen_width // 2
|
||||||
|
self.mouse_y = screen_height // 2
|
||||||
|
|
||||||
|
mouse_found = False
|
||||||
|
if self.find_mouse():
|
||||||
|
mouse_found = True
|
||||||
|
else:
|
||||||
|
self._logger.debug("WARNING: Mouse not found after multiple attempts.")
|
||||||
|
self._logger.debug("The application will run, but mouse control may not work.")
|
||||||
|
|
||||||
|
|
||||||
|
def find_mouse(self):
|
||||||
|
"""Find the mouse device with multiple retry attempts"""
|
||||||
|
MAX_ATTEMPTS = 5
|
||||||
|
RETRY_DELAY = 1 # seconds
|
||||||
|
|
||||||
|
if not usb_available:
|
||||||
|
self._logger.debug("USB library not available; cannot find mouse.")
|
||||||
|
return False
|
||||||
|
|
||||||
|
for attempt in range(MAX_ATTEMPTS):
|
||||||
|
try:
|
||||||
|
self._logger.debug(f"Mouse detection attempt {attempt+1}/{MAX_ATTEMPTS}")
|
||||||
|
|
||||||
|
# Constants for USB control transfers
|
||||||
|
DIR_OUT = 0
|
||||||
|
# DIR_IN = 0x80 # Unused variable
|
||||||
|
REQTYPE_CLASS = 1 << 5
|
||||||
|
REQREC_INTERFACE = 1 << 0
|
||||||
|
HID_REQ_SET_PROTOCOL = 0x0B
|
||||||
|
|
||||||
|
# Find all USB devices
|
||||||
|
devices_found = False
|
||||||
|
|
||||||
|
for device in usb.core.find(find_all=True):
|
||||||
|
devices_found = True
|
||||||
|
self._logger.debug(f"Found device: {device.idVendor:04x}:{device.idProduct:04x}")
|
||||||
|
|
||||||
|
try:
|
||||||
|
# Try to get device info
|
||||||
|
try:
|
||||||
|
manufacturer = device.manufacturer
|
||||||
|
product = device.product
|
||||||
|
except Exception: # pylint: disable=broad-except
|
||||||
|
manufacturer = "Unknown"
|
||||||
|
product = "Unknown"
|
||||||
|
|
||||||
|
# Just use whatever device we find
|
||||||
|
self.mouse = device
|
||||||
|
|
||||||
|
# Try to detach kernel driver
|
||||||
|
try:
|
||||||
|
has_kernel_driver = hasattr(device, 'is_kernel_driver_active')
|
||||||
|
if has_kernel_driver and device.is_kernel_driver_active(0):
|
||||||
|
device.detach_kernel_driver(0)
|
||||||
|
except Exception as e: # pylint: disable=broad-except
|
||||||
|
self._logger.debug(f"Error detaching kernel driver: {e}")
|
||||||
|
|
||||||
|
# Set configuration
|
||||||
|
try:
|
||||||
|
device.set_configuration()
|
||||||
|
except Exception as e: # pylint: disable=broad-except
|
||||||
|
self._logger.debug(f"Error setting configuration: {e}")
|
||||||
|
continue # Try next device
|
||||||
|
|
||||||
|
# Just assume endpoint 0x81 (common for mice)
|
||||||
|
self.in_endpoint = 0x81
|
||||||
|
self._logger.debug(f"Using mouse: {manufacturer}, {product}")
|
||||||
|
|
||||||
|
# Set to report protocol mode
|
||||||
|
try:
|
||||||
|
bmRequestType = DIR_OUT | REQTYPE_CLASS | REQREC_INTERFACE
|
||||||
|
bRequest = HID_REQ_SET_PROTOCOL
|
||||||
|
wValue = 1 # 1 = report protocol
|
||||||
|
wIndex = 0 # First interface
|
||||||
|
|
||||||
|
buf = bytearray(1)
|
||||||
|
device.ctrl_transfer(bmRequestType, bRequest, wValue, wIndex, buf)
|
||||||
|
self._logger.debug("Set to report protocol mode")
|
||||||
|
except Exception as e: # pylint: disable=broad-except
|
||||||
|
self._logger.debug(f"Could not set protocol: {e}")
|
||||||
|
|
||||||
|
# Buffer for reading data
|
||||||
|
self.buf = array.array("B", [0] * 4)
|
||||||
|
self._logger.debug("Created 4-byte buffer for mouse data")
|
||||||
|
|
||||||
|
# Verify mouse works by reading from it
|
||||||
|
try:
|
||||||
|
# Try to read some data with a short timeout
|
||||||
|
data = device.read(self.in_endpoint, self.buf, timeout=100)
|
||||||
|
self._logger.debug(f"Mouse test read successful: {data} bytes")
|
||||||
|
return True
|
||||||
|
except usb.core.USBTimeoutError:
|
||||||
|
# Timeout is normal if mouse isn't moving
|
||||||
|
self._logger.debug("Mouse connected but not sending data (normal)")
|
||||||
|
return True
|
||||||
|
except Exception as e: # pylint: disable=broad-except
|
||||||
|
self._logger.debug(f"Mouse test read failed: {e}")
|
||||||
|
# Continue to try next device or retry
|
||||||
|
self.mouse = None
|
||||||
|
self.in_endpoint = None
|
||||||
|
continue
|
||||||
|
|
||||||
|
except Exception as e: # pylint: disable=broad-except
|
||||||
|
self._logger.debug(f"Error initializing device: {e}")
|
||||||
|
continue
|
||||||
|
|
||||||
|
if not devices_found:
|
||||||
|
self._logger.debug("No USB devices found")
|
||||||
|
|
||||||
|
# If we get here without returning, no suitable mouse was found
|
||||||
|
self._logger.debug(f"No working mouse found on attempt {attempt+1}, retrying...")
|
||||||
|
gc.collect()
|
||||||
|
time.sleep(RETRY_DELAY)
|
||||||
|
|
||||||
|
except Exception as e: # pylint: disable=broad-except
|
||||||
|
self._logger.debug(f"Error during mouse detection: {e}")
|
||||||
|
gc.collect()
|
||||||
|
time.sleep(RETRY_DELAY)
|
||||||
|
|
||||||
|
self._logger.debug("Failed to find a working mouse after multiple attempts")
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
def poll(self):
|
||||||
|
"""Check for input. Returns contact (a bool), False (no button B),
|
||||||
|
and it's location ((x,y) or None)"""
|
||||||
|
|
||||||
|
if self._process_mouse_input():
|
||||||
|
self._cursor_grp.x = self.mouse_x - self._x_offset
|
||||||
|
self._cursor_grp.y = self.mouse_y - self._y_offset
|
||||||
|
return self.left_button_pressed, self.right_button_pressed, \
|
||||||
|
(self.mouse_x, self.mouse_y)
|
||||||
|
else:
|
||||||
|
return False, False, None
|
||||||
|
|
||||||
|
def poke(self, location=None):
|
||||||
|
"""Force a bitmap refresh."""
|
||||||
|
self._display_grp.remove(self._cursor_grp)
|
||||||
|
if location is not None:
|
||||||
|
self._cursor_grp.x = location[0] - self._x_offset
|
||||||
|
self._cursor_grp.y = location[1] - self._y_offset
|
||||||
|
self._display_grp.append(self._cursor_grp)
|
||||||
|
|
||||||
|
def set_cursor_bitmap(self, bmp):
|
||||||
|
"""Update the cursor bitmap.
|
||||||
|
|
||||||
|
:param bmp: the new cursor bitmap
|
||||||
|
"""
|
||||||
|
self._cursor_grp.remove(self._cur_sprite)
|
||||||
|
self._cur_sprite = displayio.TileGrid(bmp, pixel_shader=self._cur_palette)
|
||||||
|
self._cursor_grp.append(self._cur_sprite)
|
||||||
|
self.poke()
|
||||||
|
|
||||||
|
def _process_mouse_input(self):
|
||||||
|
"""Process mouse input - simplified version without wheel support"""
|
||||||
|
|
||||||
|
try:
|
||||||
|
# Attempt to read data from the mouse (10ms timeout)
|
||||||
|
count = self.mouse.read(self.in_endpoint, self.buf, timeout=10)
|
||||||
|
|
||||||
|
except usb.core.USBTimeoutError:
|
||||||
|
# Timeout is normal if mouse isn't moving
|
||||||
|
return False
|
||||||
|
except usb.core.USBError as e:
|
||||||
|
# Handle timeouts silently
|
||||||
|
if e.errno == 110: # Operation timed out
|
||||||
|
return False
|
||||||
|
|
||||||
|
# Handle disconnections
|
||||||
|
if e.errno == 19: # No such device
|
||||||
|
self._logger.debug("Mouse disconnected")
|
||||||
|
self.mouse = None
|
||||||
|
self.in_endpoint = None
|
||||||
|
gc.collect()
|
||||||
|
|
||||||
|
return False
|
||||||
|
except Exception as e: # pylint: disable=broad-except
|
||||||
|
self._logger.debug(f"Error reading mouse: {type(e).__name__}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
if count >= 3: # We need at least buttons, X and Y
|
||||||
|
# Extract mouse button states
|
||||||
|
buttons = self.buf[0]
|
||||||
|
x = self.buf[1]
|
||||||
|
y = self.buf[2]
|
||||||
|
|
||||||
|
# Convert to signed values if needed
|
||||||
|
if x > 127:
|
||||||
|
x = x - 256
|
||||||
|
if y > 127:
|
||||||
|
y = y - 256
|
||||||
|
|
||||||
|
# Extract button states
|
||||||
|
current_left_button_state = buttons & 0x01
|
||||||
|
current_right_button_state = (buttons & 0x02) >> 1
|
||||||
|
|
||||||
|
# Detect button presses
|
||||||
|
if current_left_button_state == 1 and self.last_left_button_state == 0:
|
||||||
|
self.left_button_pressed = True
|
||||||
|
elif current_left_button_state == 0 and self.last_left_button_state == 1:
|
||||||
|
self.left_button_pressed = False
|
||||||
|
|
||||||
|
if current_right_button_state == 1 and self.last_right_button_state == 0:
|
||||||
|
self.right_button_pressed = True
|
||||||
|
elif current_right_button_state == 0 and self.last_right_button_state == 1:
|
||||||
|
self.right_button_pressed = False
|
||||||
|
|
||||||
|
# Update button states
|
||||||
|
self.last_left_button_state = current_left_button_state
|
||||||
|
self.last_right_button_state = current_right_button_state
|
||||||
|
|
||||||
|
# Update position
|
||||||
|
self.mouse_x += x
|
||||||
|
self.mouse_y += y
|
||||||
|
|
||||||
|
# Ensure position stays within bounds
|
||||||
|
self.mouse_x = max(0, min(self.SCREEN_WIDTH - 1, self.mouse_x))
|
||||||
|
self.mouse_y = max(0, min(self.SCREEN_HEIGHT - 1, self.mouse_y))
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
return False
|
||||||
|
|
||||||
|
################################################################################
|
||||||
|
|
||||||
|
|
||||||
class Paint(object):
|
class Paint(object):
|
||||||
def __init__(self, display=board.DISPLAY):
|
def __init__(self, display=None):
|
||||||
|
|
||||||
|
if display is None:
|
||||||
|
if hasattr(board, "DISPLAY"):
|
||||||
|
display = board.DISPLAY
|
||||||
|
else:
|
||||||
|
if request_display_config is not None:
|
||||||
|
request_display_config(320, 240)
|
||||||
|
display = supervisor.runtime.display
|
||||||
|
else:
|
||||||
|
raise RuntimeError("No display found.")
|
||||||
|
|
||||||
self._logger = logging.getLogger("Paint")
|
self._logger = logging.getLogger("Paint")
|
||||||
if not self._logger.hasHandlers():
|
if not self._logger.hasHandlers():
|
||||||
self._logger.addHandler(logging.StreamHandler())
|
self._logger.addHandler(logging.StreamHandler())
|
||||||
|
|
@ -184,7 +470,7 @@ class Paint(object):
|
||||||
)
|
)
|
||||||
self._splash.append(self._bg_sprite)
|
self._splash.append(self._bg_sprite)
|
||||||
|
|
||||||
self._palette_bitmap = displayio.Bitmap(self._w, self._h, 5)
|
self._palette_bitmap = displayio.Bitmap(self._w, self._h, 8)
|
||||||
self._palette_palette = displayio.Palette(len(Color.colors))
|
self._palette_palette = displayio.Palette(len(Color.colors))
|
||||||
for i, c in enumerate(Color.colors):
|
for i, c in enumerate(Color.colors):
|
||||||
self._palette_palette[i] = c
|
self._palette_palette[i] = c
|
||||||
|
|
@ -193,10 +479,11 @@ class Paint(object):
|
||||||
)
|
)
|
||||||
self._splash.append(self._palette_sprite)
|
self._splash.append(self._palette_sprite)
|
||||||
|
|
||||||
self._fg_bitmap = displayio.Bitmap(self._w, self._h, 5)
|
self._fg_bitmap = displayio.Bitmap(self._w, self._h, 9)
|
||||||
self._fg_palette = displayio.Palette(len(Color.colors))
|
self._fg_palette = displayio.Palette(len(Color.colors)+1)
|
||||||
for i, c in enumerate(Color.colors):
|
for i, c in enumerate(Color.colors):
|
||||||
self._fg_palette[i] = c
|
self._fg_palette[i] = c
|
||||||
|
self._fg_palette[8] = Color.PINK # Marker for filled areas
|
||||||
self._fg_sprite = displayio.TileGrid(
|
self._fg_sprite = displayio.TileGrid(
|
||||||
self._fg_bitmap, pixel_shader=self._fg_palette, x=0, y=0
|
self._fg_bitmap, pixel_shader=self._fg_palette, x=0, y=0
|
||||||
)
|
)
|
||||||
|
|
@ -226,18 +513,24 @@ class Paint(object):
|
||||||
self._poller = TouchscreenPoller(self._splash, self._cursor_bitmaps[0])
|
self._poller = TouchscreenPoller(self._splash, self._cursor_bitmaps[0])
|
||||||
elif hasattr(board, "BUTTON_CLOCK"):
|
elif hasattr(board, "BUTTON_CLOCK"):
|
||||||
self._poller = CursorPoller(self._splash, self._cursor_bitmaps[0])
|
self._poller = CursorPoller(self._splash, self._cursor_bitmaps[0])
|
||||||
|
elif usb_available:
|
||||||
|
self._poller = MousePoller(self._splash, self._cursor_bitmaps[0], self._w, self._h)
|
||||||
|
if not self._poller.mouse:
|
||||||
|
raise RuntimeError("No mouse found. Please connect a USB mouse.")
|
||||||
else:
|
else:
|
||||||
raise AttributeError("PyPaint requires a touchscreen or cursor.")
|
raise AttributeError("PyPaint requires a touchscreen or cursor.")
|
||||||
|
|
||||||
self._a_pressed = False
|
self._a_pressed = False
|
||||||
self._last_a_pressed = False
|
self._last_a_pressed = False
|
||||||
|
self._b_pressed = False
|
||||||
|
self._last_b_pressed = False
|
||||||
self._location = None
|
self._location = None
|
||||||
self._last_location = None
|
self._last_location = None
|
||||||
|
|
||||||
self._pencolor = 7
|
self._pencolor = 7
|
||||||
|
|
||||||
def _make_palette(self):
|
def _make_palette(self):
|
||||||
self._palette_bitmap = displayio.Bitmap(self._w // 10, self._h, 5)
|
self._palette_bitmap = displayio.Bitmap(self._w // 10, self._h, 8)
|
||||||
self._palette_palette = displayio.Palette(len(Color.colors))
|
self._palette_palette = displayio.Palette(len(Color.colors))
|
||||||
for i, c in enumerate(Color.colors):
|
for i, c in enumerate(Color.colors):
|
||||||
self._palette_palette[i] = c
|
self._palette_palette[i] = c
|
||||||
|
|
@ -364,6 +657,73 @@ class Paint(object):
|
||||||
else:
|
else:
|
||||||
x0 += 1
|
x0 += 1
|
||||||
|
|
||||||
|
def _fill(self, x, y, c):
|
||||||
|
"""Fill an area with the current color.
|
||||||
|
|
||||||
|
:param x: x coordinate to start filling from
|
||||||
|
:param y: y coordinate to start filling from
|
||||||
|
:param c: color to fill with
|
||||||
|
"""
|
||||||
|
MARKER = 8 # Marker for filled areas
|
||||||
|
self._logger.debug("Filling at (%d, %d) with color %d", x, y, c)
|
||||||
|
|
||||||
|
if self._fg_bitmap[x, y] != c:
|
||||||
|
blank_color = self._fg_bitmap[x, y]
|
||||||
|
self._fg_bitmap[x, y] = MARKER
|
||||||
|
done = False
|
||||||
|
min_row = 0
|
||||||
|
max_row = self._h - 1
|
||||||
|
min_col = (self._w // 10) + 1
|
||||||
|
max_col = self._w - 1
|
||||||
|
while not done:
|
||||||
|
newmin_row = self._h - 1
|
||||||
|
newmax_row = 0
|
||||||
|
newmin_col = self._w - 1
|
||||||
|
newmax_col = (self._w // 10) + 1
|
||||||
|
done = True
|
||||||
|
#self._logger.debug("Rows: %d to %d Cols: %d to %d" , min_row, max_row, min_col, max_col)
|
||||||
|
for i in range(min_row,max_row):
|
||||||
|
for j in range(min_col,max_col):
|
||||||
|
if self._fg_bitmap[j, i] == MARKER:
|
||||||
|
newmarker = False
|
||||||
|
if j > self._w // 10 and self._fg_bitmap[j - 1, i] == blank_color:
|
||||||
|
self._fg_bitmap[j - 1, i] = MARKER
|
||||||
|
newmarker = True
|
||||||
|
if j < self._w - 1 and self._fg_bitmap[j + 1, i] == blank_color:
|
||||||
|
self._fg_bitmap[j + 1, i] = MARKER
|
||||||
|
newmarker = True
|
||||||
|
if i > 0 and self._fg_bitmap[j, i - 1] == blank_color:
|
||||||
|
self._fg_bitmap[j, i - 1] = MARKER
|
||||||
|
newmarker = True
|
||||||
|
if i < self._h - 1 and self._fg_bitmap[j, i + 1] == blank_color:
|
||||||
|
self._fg_bitmap[j, i + 1] = MARKER
|
||||||
|
newmarker = True
|
||||||
|
|
||||||
|
if newmarker:
|
||||||
|
done = False
|
||||||
|
if i < newmin_row:
|
||||||
|
newmin_row = i
|
||||||
|
if i > newmax_row:
|
||||||
|
newmax_row = i
|
||||||
|
if j < newmin_col:
|
||||||
|
newmin_col = j
|
||||||
|
if j > newmax_col:
|
||||||
|
newmax_col = j
|
||||||
|
|
||||||
|
max_row = min(newmax_row + 2, self._h - 1)
|
||||||
|
min_row = max(newmin_row - 2, 0)
|
||||||
|
max_col = min(newmax_col + 2, self._w - 1)
|
||||||
|
min_col = max(newmin_col - 2, (self._w // 10) + 1)
|
||||||
|
|
||||||
|
self._poller.poke()
|
||||||
|
|
||||||
|
for i in range(self._h):
|
||||||
|
for j in range(self._w):
|
||||||
|
if self._fg_bitmap[j, i] == MARKER:
|
||||||
|
self._fg_bitmap[j, i] = c
|
||||||
|
|
||||||
|
self._poller.poke()
|
||||||
|
|
||||||
def _handle_palette_selection(self, location):
|
def _handle_palette_selection(self, location):
|
||||||
selected = location[1] // self._swatch_height
|
selected = location[1] // self._swatch_height
|
||||||
if selected >= self._number_of_palette_options:
|
if selected >= self._number_of_palette_options:
|
||||||
|
|
@ -392,6 +752,12 @@ class Paint(object):
|
||||||
def _handle_a_release(self, location):
|
def _handle_a_release(self, location):
|
||||||
self._logger.debug("A Released!")
|
self._logger.debug("A Released!")
|
||||||
|
|
||||||
|
def _handle_b_release(self, location):
|
||||||
|
self._logger.debug("B Released!")
|
||||||
|
if location[0] >= self._w // 10: # not in color picker
|
||||||
|
self._fill(location[0], location[1], self._pencolor)
|
||||||
|
self._poller.poke()
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def _was_a_just_pressed(self):
|
def _was_a_just_pressed(self):
|
||||||
return self._a_pressed and not self._last_a_pressed
|
return self._a_pressed and not self._last_a_pressed
|
||||||
|
|
@ -400,6 +766,10 @@ class Paint(object):
|
||||||
def _was_a_just_released(self):
|
def _was_a_just_released(self):
|
||||||
return not self._a_pressed and self._last_a_pressed
|
return not self._a_pressed and self._last_a_pressed
|
||||||
|
|
||||||
|
@property
|
||||||
|
def _was_b_just_released(self):
|
||||||
|
return not self._b_pressed and self._last_b_pressed
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def _did_move(self):
|
def _did_move(self):
|
||||||
if self._location is not None and self._last_location is not None:
|
if self._location is not None and self._last_location is not None:
|
||||||
|
|
@ -410,8 +780,11 @@ class Paint(object):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def _update(self):
|
def _update(self):
|
||||||
self._last_a_pressed, self._last_location = self._a_pressed, self._location
|
self._last_a_pressed = self._a_pressed
|
||||||
self._a_pressed, self._location = self._poller.poll()
|
self._last_b_pressed = self._b_pressed
|
||||||
|
if self._location is not None:
|
||||||
|
self._last_location = self._location
|
||||||
|
self._a_pressed, self._b_pressed, self._location = self._poller.poll()
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
"""Run the painting program."""
|
"""Run the painting program."""
|
||||||
|
|
@ -421,6 +794,8 @@ class Paint(object):
|
||||||
self._handle_a_press(self._location)
|
self._handle_a_press(self._location)
|
||||||
elif self._was_a_just_released:
|
elif self._was_a_just_released:
|
||||||
self._handle_a_release(self._location)
|
self._handle_a_release(self._location)
|
||||||
|
if self._was_b_just_released:
|
||||||
|
self._handle_b_release(self._last_location)
|
||||||
if self._did_move and self._a_pressed:
|
if self._did_move and self._a_pressed:
|
||||||
self._handle_motion(self._last_location, self._location)
|
self._handle_motion(self._last_location, self._location)
|
||||||
time.sleep(0.1)
|
time.sleep(0.1)
|
||||||
|
|
|
||||||
BIN
CircuitPython_PyPaint/icon.bmp
Normal file
BIN
CircuitPython_PyPaint/icon.bmp
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 2.2 KiB |
4
CircuitPython_PyPaint/metadata.json
Normal file
4
CircuitPython_PyPaint/metadata.json
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
{
|
||||||
|
"title": "PyPaint",
|
||||||
|
"icon": "icon.bmp"
|
||||||
|
}
|
||||||
Loading…
Reference in a new issue