Skip to content

Grid Layout has no contains param #91

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
DJDevon3 opened this issue Dec 14, 2023 · 9 comments
Closed

Grid Layout has no contains param #91

DJDevon3 opened this issue Dec 14, 2023 · 9 comments

Comments

@DJDevon3
Copy link

On my path of creating a touch keyboard for the TFT Featherwing after laying out the keyboard saw there's no .contains param like there are for widgets and buttons. Requesting contains add to grid layout or some advice on completing this with touch capability.

IMG_1329

forkawesome_font = bitmap_font.load_font("/fonts/forkawesome-12.pcf")
# Quick Colors for Labels
TEXT_BLACK = 0x000000
TEXT_BLUE = 0x0000FF
TEXT_CYAN = 0x00FFFF
TEXT_GRAY = 0x8B8B8B
TEXT_GREEN = 0x00FF00
TEXT_LIGHTBLUE = 0x90C7FF
TEXT_MAGENTA = 0xFF00FF
TEXT_ORANGE = 0xFFA500
TEXT_PURPLE = 0x800080
TEXT_RED = 0xFF0000
TEXT_WHITE = 0xFFFFFF
TEXT_YELLOW = 0xFFFF00

def make_my_label(font, anchor_point, anchored_position, scale, color):
    func_label = label.Label(font)
    func_label.anchor_point = anchor_point
    func_label.anchored_position = anchored_position
    func_label.scale = scale
    func_label.color = color
    return func_label

hello_label_change_wifi = make_my_label(
    terminalio.FONT, (0.5, 1.0), (DISPLAY_WIDTH / 2, 15), 1, TEXT_WHITE)
input_change_wifi = make_my_label(
    terminalio.FONT, (0.0, 0.0), (5, 50), 1, TEXT_WHITE)
input_new_cred = make_my_label(
    terminalio.FONT, (0.0, 0.0), (100, 50), 1, TEXT_WHITE)

# Grid Layout Labels. Cell invisible with no text label
_labels = []
keyboard_input =[]
_labels.append(label.Label(terminalio.FONT, scale=2, x=0, y=0, text="`"))
layout.add_content(_labels[0], grid_position=(0, 0), cell_size=(1, 1))
_labels.append(label.Label(terminalio.FONT, scale=2, x=0, y=0, text="1"))
layout.add_content(_labels[1], grid_position=(1, 0), cell_size=(1, 1))
_labels.append(label.Label(terminalio.FONT, scale=2, x=0, y=0, text="2"))
layout.add_content(_labels[2], grid_position=(2, 0), cell_size=(1, 1))
_labels.append(label.Label(terminalio.FONT, scale=2, x=0, y=0, text="3"))
layout.add_content(_labels[3], grid_position=(3, 0), cell_size=(1, 1))
_labels.append(label.Label(terminalio.FONT, scale=2, x=0, y=0, text="4"))
layout.add_content(_labels[4], grid_position=(4, 0), cell_size=(1, 1))
_labels.append(label.Label(terminalio.FONT, scale=2, x=0, y=0, text="5"))
layout.add_content(_labels[5], grid_position=(5, 0), cell_size=(1, 1))
_labels.append(label.Label(terminalio.FONT, scale=2, x=0, y=0, text="6"))
layout.add_content(_labels[6], grid_position=(6, 0), cell_size=(1, 1))
_labels.append(label.Label(terminalio.FONT, scale=2, x=0, y=0, text="7"))
layout.add_content(_labels[7], grid_position=(7, 0), cell_size=(1, 1))
_labels.append(label.Label(terminalio.FONT, scale=2, x=0, y=0, text="8"))
layout.add_content(_labels[8], grid_position=(8, 0), cell_size=(1, 1))
_labels.append(label.Label(terminalio.FONT, scale=2, x=0, y=0, text="9"))
layout.add_content(_labels[9], grid_position=(9, 0), cell_size=(1, 1))
_labels.append(label.Label(terminalio.FONT, scale=2, x=0, y=0, text="0"))
layout.add_content(_labels[10], grid_position=(10, 0), cell_size=(1, 1))
_labels.append(label.Label(terminalio.FONT, scale=2, x=0, y=0, text="-"))
layout.add_content(_labels[11], grid_position=(11, 0), cell_size=(1, 1))
_labels.append(label.Label(terminalio.FONT, scale=2, x=0, y=0, text="="))
layout.add_content(_labels[12], grid_position=(12, 0), cell_size=(1, 1))
_labels.append(label.Label(forkawesome_font, scale=1, x=0, y=0, text="\uf0e2"))
layout.add_content(_labels[13], grid_position=(13, 0), cell_size=(1, 1))

_labels.append(label.Label(terminalio.FONT, scale=2, x=0, y=0, text="Q"))
layout.add_content(_labels[14], grid_position=(0, 1), cell_size=(1, 1))
_labels.append(label.Label(terminalio.FONT, scale=2, x=0, y=0, text="W"))
layout.add_content(_labels[15], grid_position=(1, 1), cell_size=(1, 1))
_labels.append(label.Label(terminalio.FONT, scale=2, x=0, y=0, text="E"))
layout.add_content(_labels[16], grid_position=(2, 1), cell_size=(1, 1))
_labels.append(label.Label(terminalio.FONT, scale=2, x=0, y=0, text="R"))
layout.add_content(_labels[17], grid_position=(3, 1), cell_size=(1, 1))
_labels.append(label.Label(terminalio.FONT, scale=2, x=0, y=0, text="T"))
layout.add_content(_labels[18], grid_position=(4, 1), cell_size=(1, 1))
_labels.append(label.Label(terminalio.FONT, scale=2, x=0, y=0, text="Y"))
layout.add_content(_labels[19], grid_position=(5, 1), cell_size=(1, 1))
_labels.append(label.Label(terminalio.FONT, scale=2, x=0, y=0, text="U"))
layout.add_content(_labels[20], grid_position=(6, 1), cell_size=(1, 1))
_labels.append(label.Label(terminalio.FONT, scale=2, x=0, y=0, text="I"))
layout.add_content(_labels[21], grid_position=(7, 1), cell_size=(1, 1))
_labels.append(label.Label(terminalio.FONT, scale=2, x=0, y=0, text="O"))
layout.add_content(_labels[22], grid_position=(8, 1), cell_size=(1, 1))
_labels.append(label.Label(terminalio.FONT, scale=2, x=0, y=0, text="P"))
layout.add_content(_labels[23], grid_position=(9, 1), cell_size=(1, 1))
_labels.append(label.Label(terminalio.FONT, scale=2, x=0, y=0, text="["))
layout.add_content(_labels[24], grid_position=(10, 1), cell_size=(1, 1))
_labels.append(label.Label(terminalio.FONT, scale=2, x=0, y=0, text="]"))
layout.add_content(_labels[25], grid_position=(11, 1), cell_size=(1, 1))
_labels.append(label.Label(terminalio.FONT, scale=2, x=0, y=0, text="\\"))
layout.add_content(_labels[26], grid_position=(12, 1), cell_size=(2, 1))

_labels.append(label.Label(terminalio.FONT, scale=2, x=0, y=0, text="A"))
layout.add_content(_labels[27], grid_position=(0, 2), cell_size=(1, 1))
_labels.append(label.Label(terminalio.FONT, scale=2, x=0, y=0, text="S"))
layout.add_content(_labels[28], grid_position=(1, 2), cell_size=(1, 1))
_labels.append(label.Label(terminalio.FONT, scale=2, x=0, y=0, text="D"))
layout.add_content(_labels[29], grid_position=(2, 2), cell_size=(1, 1))
_labels.append(label.Label(terminalio.FONT, scale=2, x=0, y=0, text="F"))
layout.add_content(_labels[30], grid_position=(3, 2), cell_size=(1, 1))
_labels.append(label.Label(terminalio.FONT, scale=2, x=0, y=0, text="G"))
layout.add_content(_labels[31], grid_position=(4, 2), cell_size=(1, 1))
_labels.append(label.Label(terminalio.FONT, scale=2, x=0, y=0, text="H"))
layout.add_content(_labels[32], grid_position=(5, 2), cell_size=(1, 1))
_labels.append(label.Label(terminalio.FONT, scale=2, x=0, y=0, text="J"))
layout.add_content(_labels[33], grid_position=(6, 2), cell_size=(1, 1))
_labels.append(label.Label(terminalio.FONT, scale=2, x=0, y=0, text="K"))
layout.add_content(_labels[34], grid_position=(7, 2), cell_size=(1, 1))
_labels.append(label.Label(terminalio.FONT, scale=2, x=0, y=0, text="L"))
layout.add_content(_labels[35], grid_position=(8, 2), cell_size=(1, 1))
_labels.append(label.Label(terminalio.FONT, scale=2, x=0, y=0, text=";"))
layout.add_content(_labels[36], grid_position=(9, 2), cell_size=(1, 1))
_labels.append(label.Label(terminalio.FONT, scale=2, x=0, y=0, text="'"))
layout.add_content(_labels[37], grid_position=(10, 2), cell_size=(1, 1))
_labels.append(label.Label(terminalio.FONT, scale=2, x=0, y=0, text="ENTER"))
layout.add_content(_labels[38], grid_position=(11, 2), cell_size=(3, 1))

_labels.append(label.Label(terminalio.FONT, scale=2, x=0, y=0, text="Z"))
layout.add_content(_labels[39], grid_position=(0, 3), cell_size=(1, 1))
_labels.append(label.Label(terminalio.FONT, scale=2, x=0, y=0, text="X"))
layout.add_content(_labels[40], grid_position=(1, 3), cell_size=(1, 1))
_labels.append(label.Label(terminalio.FONT, scale=2, x=0, y=0, text="C"))
layout.add_content(_labels[41], grid_position=(2, 3), cell_size=(1, 1))
_labels.append(label.Label(terminalio.FONT, scale=2, x=0, y=0, text="V"))
layout.add_content(_labels[42], grid_position=(3, 3), cell_size=(1, 1))
_labels.append(label.Label(terminalio.FONT, scale=2, x=0, y=0, text="B"))
layout.add_content(_labels[43], grid_position=(4, 3), cell_size=(1, 1))
_labels.append(label.Label(terminalio.FONT, scale=2, x=0, y=0, text="N"))
layout.add_content(_labels[44], grid_position=(5, 3), cell_size=(1, 1))
_labels.append(label.Label(terminalio.FONT, scale=2, x=0, y=0, text="M"))
layout.add_content(_labels[45], grid_position=(6, 3), cell_size=(1, 1))
_labels.append(label.Label(terminalio.FONT, scale=2, x=0, y=0, text=","))
layout.add_content(_labels[46], grid_position=(7, 3), cell_size=(1, 1))
_labels.append(label.Label(terminalio.FONT, scale=2, x=0, y=0, text=","))
layout.add_content(_labels[47], grid_position=(8, 3), cell_size=(1, 1))
_labels.append(label.Label(terminalio.FONT, scale=2, x=0, y=0, text="."))
layout.add_content(_labels[48], grid_position=(9, 3), cell_size=(1, 1))
_labels.append(label.Label(terminalio.FONT, scale=2, x=0, y=0, text="/"))
layout.add_content(_labels[49], grid_position=(10, 3), cell_size=(1, 1))
_labels.append(label.Label(terminalio.FONT, scale=2, x=0, y=0, text="SHIFT"))
layout.add_content(_labels[50], grid_position=(11, 3), cell_size=(3, 1))

_labels.append(label.Label(terminalio.FONT, scale=2, x=0, y=0, text="CTRL"))
layout.add_content(_labels[51], grid_position=(0, 4), cell_size=(2, 1))
_labels.append(label.Label(terminalio.FONT, scale=2, x=0, y=0, text="ALT"))
layout.add_content(_labels[52], grid_position=(2, 4), cell_size=(2, 1))
_labels.append(label.Label(terminalio.FONT, scale=2, x=0, y=0, text="SPACE"))
layout.add_content(_labels[53], grid_position=(4, 4), cell_size=(6, 1))
_labels.append(label.Label(forkawesome_font, scale=1, x=0, y=0, text="\uf060"))
layout.add_content(_labels[54], grid_position=(10, 4), cell_size=(1, 1))
_labels.append(label.Label(forkawesome_font, scale=1, x=0, y=0, text="\uf061"))
layout.add_content(_labels[55], grid_position=(11, 4), cell_size=(1, 1))
_labels.append(label.Label(forkawesome_font, scale=1, x=0, y=0, text="\uf062"))
layout.add_content(_labels[56], grid_position=(12, 4), cell_size=(1, 1))
_labels.append(label.Label(forkawesome_font, scale=1, x=0, y=0, text="\uf063"))
layout.add_content(_labels[57], grid_position=(13, 4), cell_size=(1, 1))

# bunch of stuff and while True:

while display.root_group is wifi_change_group:
        label_list1 = []
        New_Label_list =[]
        hello_label_change_wifi.text = "Wifi Change Credentials"
        input_change_wifi.text = "New Password: "
        for label in _labels:
            label_list1.append(label)
        for label in label_list1:
            sorted_labels = {'text':label.text}
            New_Label_list.append([sorted_labels])
        jsonLabelList = json.dumps(New_Label_list)
        jsonlabel_list = json.loads(jsonLabelList)
        while (time.monotonic() - last) <= sleep_time and display.root_group is wifi_change_group:
            p = touchscreen.touch_point
            if p:
                print(f"Touch Point: {p}")
                print(f"Get Grid Cell H: {layout.get_cell((5,2)).text}")
                print(f"Get Grid Cell E: {layout.get_cell((2,1)).text}")
                print(f"Get Grid Cell L: {layout.get_cell((8,2)).text}")
                print(f"Get Grid Cell L: {layout.get_cell((8,2)).text}")
                print(f"Get Grid Cell O: {layout.get_cell((8,1)).text}")
                print("Key Pressed")
                label_list = json.dumps(_labels)
                #print(f"Label List: {label_list}")
                #print(f"jsonList: {jsonlabel_list}")
                print(f"Labels: {jsonlabel_list[0]}")
                print(f"Object Type: {type(_labels)}")
                input_new_cred.text = f"{jsonlabel_list[0][0]['text']}"
                menu_switching(wifi_change_group, main_group2, main_group, preferences_group, wifi_settings_group, rssi_group, sys_info_group, wifi_change_group)
            else:
                # Default state always running
                group_cleanup()
        last = time.monotonic()
Key Pressed
Labels: [{'text': '`'}]
Object Type: <class 'list'>
Touch Point: (461, 194, 71)
Get Grid Cell: H
Get Grid Cell: E
Get Grid Cell: L
Get Grid Cell: L
Get Grid Cell: O
Key Pressed
Labels: [{'text': '`'}]
Object Type: <class 'list'>

I have it drilled down to being able to work with the individual cells to print out the keyboard letters needed with hardcoded statements but not touch yet.

That's the gist of it. Full code can be viewed on my Github which I've been working on daily.

Having grid layout the capability for touch contains would have made this much easier. I'm all ears for ideas if there's a better way.

@FoamyGuy
Copy link
Contributor

@DJDevon3 first of all, That's looking awesome 🎉 Nice Job!

I think there are a few different ways this could work. Typically the contains() would only tell you True or False weather the given coordinates are within the bounds of the "View" object (be it a button or anything else that extends Control). With GridLayout you'd need something a bit different because you're wanting to discern which cell within the grid (if any) are located at the x,y coordinates given rather than just the True / False for the bounds of the entire Grid.

Theoretically a new kind of Label could be created that has a contains method (or contains() added to existing ones) and then your code could call contains on each label within the Grid to find out which one matches the coordinates.

Another option that wouldn't require any library modifications could be to use Buttons instead of Labels inside of your Grid cells. That way they will have a contains() function that you could call on each Button to determine which one matches the coordinates. I think you could set up the Buttons with transparent or black background and white label color so that they'll end up looking the same as a Label would visually.

With all of that being said I do think there is an opportunity for GridLayout to have a function that helps make this easier to achieve. I would opt for a name other than specifically contains() since it'll have different behavior (returning a cell instead of just True/False) So maybe somethying like grid_layout.which_cell_contains(x,y). You pass in the x,y coordinates and it returns either the cell that contains those coordinates or None if the point is outside all cells.

@DJDevon3
Copy link
Author

DJDevon3 commented Dec 15, 2023

I can add a ton of spritebuttons but they weren't my first choice for RAM reasons. There woiuld be about 100 of them plus 100 labels. While it would look nice, I fear the 1000+ other lines for displaying sensor data, weather API, MQTT, etc.. it wold be too much to handle.

Another method I thought about was a BMP background of a keyboard and adding touchpoint coordinates layered on top of the bmp. After considering those options, I figured the best scenario would be to have every grid be touch capable. Unsure if a touch keyboard is an edge case, the solution would likely be intensive no matter the approach.

This is the first time I've used grid layout to this extent. I thought something like grid_layout.which_cell_contains(x,y) was already included so this seems like a good way to go. I was having trouble attaching an x.y button point to a cell x,y location. By default a cell x,y is 1x1 and that's not enough for a touch button. Attempting to correlate a grid cell to a button x,y was more trouble than it was worth. A new method would be very appreciated!

You know these libraries the most intimately so I trust you to come up with a solution that benefits everyone and not just my use case. A touch keyboard would in my opinion be a worthy inclusion to the grid layout library.

@DJDevon3
Copy link
Author

DJDevon3 commented Dec 15, 2023

Thank you for the compliment. That means a lot. I've been working on this night and day.

I have previously successfully implemented SpriteButtons with a popup menu. I'm not unfamiliar with them. Grid layout seemed the best method for the keyboard layout vs a ton of SpriteButtons. I know I can do it but also know it's going to take the page 30 seconds to load. Before a new page loads the previous page must pop() or remove() the previous layers. The more labels and images on the previous page the more time it takes to unload and then load the new layers.

This page can take up to 30 seconds to load and unload
IMG_1321

This page loads almost instantly because there's almost nothing on it.
IMG_1324

@FoamyGuy
Copy link
Contributor

@DJDevon3 Sorry, I did not mean to use Buttons instead of GridLayout, but rather Buttons with the GridLayout. Essentially using Button's inside of each cell of the Grid rather than a Label.

Anyhow, while I do think that route is possible. I do think there's opportunity for a GridLayout enhancement to make an easier way to do it and not require the Buttons.

I've copied out your keyboard layout code to use for developing a proof of concept of the functionality. I'm intending to work on that during Deep Dive tonight if you're interested in watching along.

@DJDevon3
Copy link
Author

@FoamyGuy I've been up all night trying different things with no progress and might not be awake to watch the stream. :( Wouldn't buttons inside the grid layout create just as many labels or is it 1 button reused per cell? That would definitely be more efficient! Wish I knew you'd be covering this already took some sleeping pills. Hope you use the whole feather weather script so you can see how much work I've put into it... and perhaps some ways to improve it? I'm particularly proud of getting the RSSI scan to work properly. The plan is to make SSID's s electable to change networks like a mobie phone does. It's a work in progress. Hope you'd get a chuckle out of my use of literal uppercase SHOUTY named variables, did that just for you. :P

@FoamyGuy
Copy link
Contributor

In that other approach, it would need to be 1 Button per cell, they could not be re-used. And indeed each button contains a Label so in the end it will use just as many Labels in total. But the benefit would be that on those buttons you could call their contains method to test touch points and determine if they were on that item.

I cannot use the full script right now because I don't have the right hardware for everything that' sin your project. I'm using a PyPortal Titano which has the same screen size as the Featherwing you're project uses. But it doesn't have native wifi, and I don't have the the other componenets to connect to it, nor the openweather / mqtt infrastructure set up at the moment.

I'll try to get the full thing put together at some point to give it a go, but for now I must work wit only the Grid.

@DJDevon3
Copy link
Author

DJDevon3 commented Dec 15, 2023

Sounds good. I'm drinking coffee and will be awake. Sugar, lots of sugar. Cant' wait to see what you come up with.

One of the nice advantages of grid layout is it easily divides screen.wdith or screen.height. With SpriteButtons you must place the x,y exactly and that gets very tedious for something that needs 60+ buttons

@DJDevon3
Copy link
Author

DJDevon3 commented Dec 17, 2023

Running your example but I'm getting multiple key presses where as you seem to get single presses. Unsure if that's a difference within the other libraries I'm using. I used it in my full script and didn't try for the minimal version you made. Will try your softkbd version by itself next. Was able to get it to work somewhat with my full script though. I like the name soft kbd name you came up with too. You did more in a single session than I could do in weeks. Thank you for the feature addition!

I'm also running into layer issues with my full script. Apparently hidden=True doesn't mean the button is gone. So my popout menu is still touchable even though it's hidden. Have a lot of groups and layers to fix. That's a me problem though.

Works as intended in 8.2.7 using your .py to override the .mpy 👍

@FoamyGuy
Copy link
Contributor

This was resolved by #92 and resulted in https://github.com/FoamyGuy/CircuitPython_SoftKeyboard

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants