Mote hyperpixel gui


#1

I am trying to create a gui for mote which will be controlled by hyper pixel.

So far I have this


from guizero import *
from mote import Mote

mote = Mote()

mote.configure_channel(1, 16, False)
mote.configure_channel(2, 16, False)
mote.configure_channel(3, 16, False)
mote.configure_channel(4, 16, False)
app = App(title="Mote", layout="grid",  height=480, width=800)

def update_blinkt(v):
    for channel, rgb in enumerate(sliders):
        r, g, b = (slider.get() for slider in rgb)
        for pixel in range(16):
            mote.set_pixel(channel+1, pixel, r, g, b)
    mote.show()

sliders = [
    [Slider(app, command=update_blinkt, start=0, end=255, grid=[channel, rgb]) for rgb in range(3)]
    for channel in range(4)
]

app.display()

which works but is too small for hyper pixel.

I was wondering if there was another way to do this or of there was another way to do it (@gadgetoid ie the controll panel you showed on the hyperpixel bilge tank)

Thank you in advance


#2

The control panel I demoed was - IIRC - part of the Python Multitouch library and examples I wrote for the official touchscreen, see: https://github.com/pimoroni/python-multitouch


#3

that is great

however when i run any of the examples i get an error

I get a runtime error saying it is unable to locate a touchscreen device


#4

Yeah it’s designed first and foremost for the official touchscreen, you’ll have to tweak this line:

To “Touchscreen”


#6

Ok that has worked greatr.

I have made some progress and my code now looks like this.

import sys
import pygame
import time
import os
import math
from pygame.locals import *
from ft5406 import Touchscreen
from gui import widgets, Button, Slider, Dial, render_widgets, touchscreen_event
from mote import Mote

mote = Mote()

mote.configure_channel(1, 16, False)
mote.configure_channel(2, 16, False)
mote.configure_channel(3, 16, False)
mote.configure_channel(4, 16, False)

mote.clear()
pygame.init()

size = width, height = 800, 480
screen = pygame.display.set_mode(size, pygame.FULLSCREEN)

pygame.mouse.set_visible(False)

ts = Touchscreen()

for touch in ts.touches:
    touch.on_press = touchscreen_event
    touch.on_release = touchscreen_event
    touch.on_move = touchscreen_event


def my_exit(b, e, t):
    global running
    running = False

def mote_on(b,e,t):
    mote.clear()
    for channel in range(1, 5):
        for pixel in range(16):
            mote.set_pixel(channel, pixel, 255, 255, 255)
    mote.show()

def mote_off(b,e,t):
    mote.clear()
    for channel in range(1, 5):
        for pixel in range(16):
            mote.set_pixel(channel, pixel,0,0,0)
    mote.show()
Button(
    "ON",
    (0, 255, 0),
    (20, 20),
    (220, 220), 
    mote_on)

Button(
    "OFF",
    (255,0,0), 
    (20, 250),
    (220, 220), 
    mote_off)


Button(
    "x",
    (255, 0, 0),
    (760, 0),
    (40, 40),
    my_exit)

for x in range(3):
    if x == 0:
        colour=(255,0,0)
    elif x == 1:
        colour=(0,255,0)
    elif x == 2:
        colour=(0,0,255)
    Slider(
        (0,100,0),
        colour,
        (270 + (x*175), 20),
        (100, 450),
        None)


ts.run()

running = True
while running:
    for event in pygame.event.get():
        if event.type == KEYDOWN and event.key == K_ESCAPE:
            ts.stop()
            sys.exit()

    screen.fill((0, 0, 0))

    render_widgets(screen)

    pygame.display.flip()

    time.sleep(0.01)

ts.stop()

I was wondering if i would be able to get 3 seperate values from the sliders ending up in a

functioni looking like this

def change_mote():
r,g,b= wizardry to get values
mote.clear()
for channel in range(1, 5):
for pixel in range(16):
mote.set_pixel(channel, pixel, r,g,b)
mote.show()

anyway to get the values from the sliders would be helpful!


#7

I’d pass a function to your sliders on construction that sets their value to a global variable, and just grab that in your main loop periodically to update the colour of Mote.


#8

I am not sure I really understand what you mean.

How do I link the function to the sliders so I get 3 values?


#9

Let me see if I can translate a little of Gadgetoid’s magic here:

You would first need to set a global function as a starting point for your Mote colour, say glob_colour = 100,50,20, and then update that value to the current value set on your slider, whether that be red, green or blue. For instance, and this is just pseudo code:

glob_colour = 100, 50, 20 # Set a starting colour

def set_mote_value():
    global glob_colour
    for channel in range(1,5):
        for pixel in range(16):
            r, g, b = glob_colour # Set the lights to the default colour.

def mote_slider(colour): # Your Mote slider function.
    global glob_colour # Get the global variable
    glob_colour = [your slider's value] # Set your slider's value to the global variable
    
while True: # Your main loop!
set_mote_value() # Periodically update the slider's value

Hopefully that cleared things up a little! :)


#10

That’s great and should work perfectly. Do you know how to get the values from the slider in my code in the format you used

r,g,b

At the moment I can’t seem to get any values from my sliders at all!


#11

I’m afraid that I’ve never used the GUI module, and I can’t find much documentation on it either. You may have to question Mr Gee over there about that.


#12

I presume it is similar to the buttons but after numerous trials and errors I still haven’t found a way. Any insight @gadgetoid


#13

The last None argument of each slider is the on_change= callback.

This is a function with the prototype:

def on_change(object, value):
    pass

The first argument being the instance of the Slider that triggered the change (you can inspect that slider and use object.color.index(255) to get 0, 1 or 2 for (Red, Green, Blue) sliders.

So if your on_change method looked like:

mote_colour = [0,0,0]

def on_change(slider, value):
   index = slider.color.index(255)
   mote_colour[index] = value

And pass on_change into Slider instead of None.

Then when you moved each slider, your mote_colour value would change accordingly.

Finally you’d need to use your new mote_colour variable to change the colour of Mote in your main loop.


#14

Ok so I am back home and have given it a go

the code is:

#imports
import sys
import pygame
import time
import os
import math
from pygame.locals import *
from ft5406 import Touchscreen
from gui import widgets, Button, Slider, Dial, render_widgets, touchscreen_event
from mote import Mote

mote = Mote()

#set up mote
mote.configure_channel(1, 16, False)
mote.configure_channel(2, 16, False)
mote.configure_channel(3, 16, False)
mote.configure_channel(4, 16, False)

mote.clear()

#create window
pygame.init()

size = width, height = 800,480
screen = pygame.display.set_mode(size, pygame.FULLSCREEN)

pygame.mouse.set_visible(False)

ts = Touchscreen()

for touch in ts.touches:
    touch.on_press = touchscreen_event
    touch.on_release = touchscreen_event
    touch.on_move = touchscreen_event

#functions for buttons
def my_exit(b, e, t):
    global running
    running = False

def mote_on(b,e,t):
    mote.clear()
    for channel in range(1, 5):
        for pixel in range(16):
            mote.set_pixel(channel, pixel, 255, 255, 255)
    mote.show()

def mote_off(b,e,t):
    mote.clear()
    for channel in range(1, 5):
        for pixel in range(16):
            mote.set_pixel(channel, pixel,0,0,0)
    mote.show()

#function for sliders    
mote_colour = [0,0,0]

def on_change(slider, value):
   index = slider.color.index(255)
   mote_colour[index] = value

   
#create buttons    
Button(
    "ON",
    (0, 255, 0),
    (20, 20),
    (220, 220), 
    mote_on)

Button(
    "OFF",
    (255,0,0), 
    (20, 250),
    (220, 220), 
    mote_off)


Button(
    "x",
    (255, 0, 0),
    (760, 0),
    (40, 40),
    my_exit)

#create 3 sliders of different colours
for x in range(3):
    if x == 0:
        colour=(255,0,0)
    elif x == 1:
        colour=(0,255,0)
    elif x == 2:
        colour=(0,0,255)
    Slider(
        (0,255,0),
        colour,
        (270 + (x*175), 20),
        (100, 450),
        None)


ts.run()

running = True
while running:
    for event in pygame.event.get():
        if event.type == KEYDOWN and event.key == K_ESCAPE:
            ts.stop()
            sys.exit()

    screen.fill((0, 0, 0))

    render_widgets(screen)

    pygame.display.flip()

    for channel in range(1, 5):
        for pixel in range(16):
            mote.set_pixel(channel, pixel, mote_colour[0],mote_colour[1],mote_colour[2])
    mote.show()

    time.sleep(0.01)

ts.stop()

the problem is the colour of the mote doesn’t change


#15

I believe that is because your if statements and “Slider” definition are not a function, and is instead in the main code.

First, your slider gets the value of the colour, and then you head into a finite loop where by you update your display using Pygame, however, the value of “colour” is only ever updated once, since it is outside of the main loop. Therefore, in my limited understanding, your call to update the colour inside of the main loop never gets and updated value for colour other than the first one it receives, which is outside the loop.

I think…


#16

So do I need to put the slider stuff the function and the update code in one function which I then call in the main loop?


#17

Yeah, that should work. As long as you call a function within the main loop that updates the “colour” variable, it will probably be fine.


#18

Actually looking over the code I think that the on_change function gets run everytime the slider value changes which i think should update the colour variable.

I think

This is my current assumption seeing as wrapping it all in a function which is then run in the loop has not worked

Any other ideas

thanks for the help btw!


#19

Only other than the fact that Phil said:

However, I noticed on your slider function, you’re still passing None:


#20

Doh! I thought I had done that

That worked along with making sure everything was an integer

Thanks for the help