User avatar
bensimmo
Posts: 2617
Joined: Sun Dec 28, 2014 3:02 pm
Location: East Yorkshire

GuiZero & the SenseHAT

Sat Jan 13, 2018 12:12 pm

I had a play with GuiZero again since it's v0.4 was released last month and 'clickable waffles' now allows me to do something I wanted to do from the start when guizero came and I notice the waffle had been advanced, notably it is clickable.

This is so simple yet soooo satisfying. Not mega coding, not even something that is that taxing to think of

I adapted the example https://github.com/lawsie/guizero/blob/ ... _waffle.py

to an 8x8 grid,
and added the sense_hat (or sense_emu, which makes it even more flexible for teaching)
then just get it to colour the grid when clicked

Code: Select all

from guizero import App, Waffle, PushButton
from sense_hat import SenseHat

sense=SenseHat()
sense.clear(255,255,255)

def change_pixel(x,y):
    if big_waffle.get_pixel(x,y) == "white":
        big_waffle.set_pixel(x, y, "red")
        sense.set_pixel( x,y, [255,0,0] )
    else:
        big_waffle.set_pixel(x, y, "white")
        sense.set_pixel( x,y, [255,255,255] )
        

app = App("Waffle!", height=20*20, width=20*20)

big_waffle = Waffle(app, height=8, width=8, dim=25, pad=2, dotty=True, command=change_pixel)
enable_button = PushButton(app, big_waffle.enable, text="Enable")
disable_button = PushButton(app, big_waffle.disable, text="Disable")

app.display()

While guizero doesn't do colour in RGB, but predetermined word strings and hex, I did suggest RGB be added, I forget where there led too, anyway this is the reason I suggested it. That and we teach the easy scale of 0 of 255 but not the hex confloption in maths and science ;-)

They both work well together.
GuiZero can control the display.
SenseHAT can give GuiZero it's sensor to create a display for.
Sense_emu can also be used, though does lack the bright light 'wow' factor

Extensions (off the top of my head)
-Add a clear(0,0,0) on windows close ;-)
-Change the colour for the click, e.g. RGB sliders
-Create an image in the waffle and see it on screen/hat, then figure out a way of exporting the colours (get_pixels?) in to a cut and paste list for use in SenseHat code.
-Sensor display, since sense-emu is built in to Raspberry Pi Desktop Raspbian/x86
-Text in a text box and display it on the SenseHat.


SenseHAT LED https://pythonhosted.org/sense-hat/api/#led-matrix
GuiZero Waffle https://lawsie.github.io/guizero/waffle/

Note to self/ check code as it's from memory so technically untested, mainly the waffle grid and size.
/Code checked and added the missed off set_pixel, oops

User avatar
bensimmo
Posts: 2617
Joined: Sun Dec 28, 2014 3:02 pm
Location: East Yorkshire

Re: GuiZero & the SenseHAT

Wed Jan 17, 2018 12:32 pm

Added the much needed clear LED on close, save the eyes
Added a RGBtoHEX function thanks to https://github.com/lawsie/guizero/issues/31
and the Sliders to go with it RGB (top to bottom, just add labels), maybe even a single waffle to display the colour
and a reset display.
- possible changes alter if big_waffle.get_pixel(x,y) == "white": to clear on same colour not just any colour

Code: Select all

from guizero import App, Waffle, PushButton, Slider
from sense_hat import SenseHat

def RGBtoHEX():
    return "#%02x%02x%02x" % (Red_slider.value, Green_slider.value, Blue_slider.value)

def change_pixel(x,y):    
    if big_waffle.get_pixel(x,y) == "white":
        big_waffle.set_pixel(x, y, RGBtoHEX())
        sense.set_pixel(x,y, [Red_slider.value, Green_slider.value, Blue_slider.value])
    else:
        big_waffle.set_pixel(x, y, "white")
        sense.set_pixel(x,y, [255,255,255])

def reset_display():
    sense.clear(255,255,255)
    big_waffle.set_all("white")

def close_app():
    sense.clear()
    app.destroy()

#--Start of code, setup SenseHAT, set to all white display
sense=SenseHat()
sense.clear(255,255,255)

#---
app = App("SenseHat Waffle", height=20*20, width=20*20)

big_waffle = Waffle(app, height=8, width=8, dim=20, pad=2, dotty=True, color="white", command=change_pixel)
reset_button = PushButton(app, reset_display, text="Reset display")

Red_slider = Slider(app, end=255)
Green_slider = Slider(app, end=255)
Blue_slider = Slider(app, end=255)
#---

app.on_close(close_app)    #Close app and clear SenseHat display to blank. 

app.display()

Next task figure out that sensor display I can't get to work properly.

scotty101
Posts: 2798
Joined: Fri Jun 08, 2012 6:03 pm

Re: GuiZero & the SenseHAT

Wed Jan 17, 2018 1:13 pm

Did you see my sense_draw application?
viewtopic.php?f=32&t=202010
It has an example of converting from Hex to RGB and allows you to directly draw sprites on to the sense hat.

BTW I added the clickable waffle functionality to guizero so I'll take look to see if I can change it to accept RGB (and hex) and raise a pull request.
EDIT: Spotted your issue on guizero github. I've responded
Electronic and Computer Engineer
Pi Interests: Home Automation, IOT, Python and Tkinter

User avatar
bensimmo
Posts: 2617
Joined: Sun Dec 28, 2014 3:02 pm
Location: East Yorkshire

Re: GuiZero & the SenseHAT

Wed Jan 17, 2018 3:35 pm

Didn't see that.
Whatever the RGB->HEX HEX->RGB solution is, it needs to be guizero wide, not just in a waffle, but i'll leave that to the github chat, keep this topic clear. The quick function I used works well.

My solution for exporting the values would be a simple, since I've based it around letting the kids play and is designed to use the sense_hat (or sense_emu which all Raspberry Desktops should have) in Teaching, CodeClubs, Jam's etc.

So I would create a textbox (though it seems we cannot increase the height like a true textbox :(
then just either with a button or at the end of the change_pixel code (for constant update on change)
add
textbox.value=sense.get_pixels()
(or just as you print(sense.get_pixels()) though that kind of defeats the point of the GUI ;-) :lol: )

no extra code needed and returns the uniquely odd numbers the Sensehat converts too.
For example my white (set as 255,255,255) is stored and returned as (248,252,248). I know why they are not what they are set as, but not why R and B is different to G.
Nothing to worry about, but might be interesting explaining it to older kids.

scotty101
Posts: 2798
Joined: Fri Jun 08, 2012 6:03 pm

Re: GuiZero & the SenseHAT

Thu Jan 18, 2018 1:19 pm

bensimmo wrote:
Wed Jan 17, 2018 3:35 pm
So I would create a textbox (though it seems we cannot increase the height like a true textbox :(
Its a missing feature from guizero. There are two text entry widgets in Tkinter, Entry and Text. Entry is single line, Text is Multiline.
GuiZero only implements Entry limiting us to single line only.

You could save the array of pixel colours to a file rather than writing them to a widget.
Electronic and Computer Engineer
Pi Interests: Home Automation, IOT, Python and Tkinter

scotty101
Posts: 2798
Joined: Fri Jun 08, 2012 6:03 pm

Re: GuiZero & the SenseHAT

Thu Jan 18, 2018 1:56 pm

If you are able to modify your version of Guizero, I've created a branch that supports a textbox with multiple lines.

Example of usage shown below.

Code: Select all

from guizero import App, TextBox

def new_text():
    textbox.set("Hello Setting\nText")
    print(textbox.get())

app = App()
textbox = TextBox(app,multiline=True,height=5,text="Hello\nWorld")
textbox.clear()
textbox.value = "Hello \n World2"
textbox.append("New Line")
app.after(1000,new_text)

app.display()
Electronic and Computer Engineer
Pi Interests: Home Automation, IOT, Python and Tkinter

User avatar
bensimmo
Posts: 2617
Joined: Sun Dec 28, 2014 3:02 pm
Location: East Yorkshire

Re: GuiZero & the SenseHAT

Thu Jan 18, 2018 2:31 pm

I can do what I like to mine, these are just T&L (section we are in) ideas and resources for anyone that happens to be looking.

scotty101
Posts: 2798
Joined: Fri Jun 08, 2012 6:03 pm

Re: GuiZero & the SenseHAT

Thu Jan 18, 2018 3:42 pm

bensimmo wrote:
Thu Jan 18, 2018 2:31 pm
I can do what I like to mine, these are just T&L (section we are in) ideas and resources for anyone that happens to be looking.
Yep and I'm trying to help :D
Electronic and Computer Engineer
Pi Interests: Home Automation, IOT, Python and Tkinter

User avatar
bensimmo
Posts: 2617
Joined: Sun Dec 28, 2014 3:02 pm
Location: East Yorkshire

Re: GuiZero & the SenseHAT

Tue Jan 23, 2018 4:57 pm

Bit messy but Sensors (if compass is enabled then it locks out Yaw & Roll from orientation iirc.?)
Grid doesn't work as I expected.
Formatting layout is not done (if it can?)
It's a start for anyone trying it.
IMU are destined for their own box.
It's tedious.


Code: Select all

from guizero import *
from sense_hat import SenseHat

sense=SenseHat()

def read_temp_sensor():
    return round(sense.get_temperature(),1)

def read_humidity_sensor():
    return round(sense.get_humidity(),1)

def read_pressure_sensor():
    return round(sense.get_pressure()*0.1,1)


def update_environment_sensors():
    count_text.value=int(count_text.value)+1
    
    temperature_text.value = read_temp_sensor(),"°C"
    humidity_text.value = read_humidity_sensor(),"%"
    pressure_text.value = read_pressure_sensor(),"kPa"
    

def update_IMU_sensors():
    orient_yaw,orient_pitch,orient_roll = sense.get_orientation().values()
    mag_x,mag_y,mag_z = sense.get_compass_raw().values()
    acc_x,acc_y,acc_z = sense.get_accelerometer_raw().values()
    gyro_x,gyro_y,gyro_z = sense.get_gyroscope_raw().values()
    #north = sense.get_compass()


    IMU_orient_yaw_text.value = round(orient_yaw,1)    
    IMU_orient_pitch_text.value = round(orient_pitch,1)
    IMU_orient_roll_text.value = round(orient_roll,1)
    
    IMU_mag_x_text.value = round(mag_x,1)
    IMU_mag_y_text.value = round(mag_y,1)
    IMU_mag_z_text.value = round(mag_z,1)
    
    IMU_acc_x_text.value = round(acc_x,1)
    IMU_acc_y_text.value = round(acc_y,1)
    IMU_acc_z_text.value = round(acc_z,1)
    
    IMU_gyro_x_text.value = round(gyro_x,1)
    IMU_gyro_y_text.value = round(gyro_y,1)
    IMU_gyro_z_text.value = round(gyro_z,1)
    
    #IMU_compass_text.value = round(north,1)
    
    
    
##-------##

if __name__ == '__main__':
    app = App(title="Sensor Display!",
              height=230,
              width=420,
              layout='grid')
    
    #start of objects
    
    title_count = Text(app, "count 'debug':", grid=[0, 0])
    count_text = Text(app, "1", grid=[1, 0])    

    
    title = Text(app, "Temperature Sensor value:", grid=[0, 1])
    temperature_text = Text(app, "xx", grid=[1, 1])

    title2 = Text(app, "Humidity Sensor value:", grid=[0, 2])
    humidity_text = Text(app, "xx", grid=[1, 2])

    title3 = Text(app, "Pressure Sensor value:", grid=[0, 3])
    pressure_text = Text(app, "xx", grid=[1, 3])
    
    #IMU box
    

    IMU_title_orient_yaw = Text(app, "Yaw", grid=[1, 5])
    IMU_title_orient_pitch = Text(app, "Pitch", grid=[2, 5])
    IMU_title_orient_roll = Text(app, "Roll", grid=[3, 5])
    
    IMU_title_orient = Text(app, "Orientation:", grid=[0, 6])
    IMU_orient_yaw_text = Text(app, "xx", grid=[1, 6])
    IMU_orient_pitch_text = Text(app, "xx", grid=[2, 6])
    IMU_orient_roll_text = Text(app, "xx", grid=[3, 6])
    
    IMU_title_x = Text(app, "X", grid=[1, 8])
    IMU_title_y = Text(app, "Y", grid=[2, 8])
    IMU_title_z = Text(app, "Z", grid=[3, 8])
    
    IMU_title_mag = Text(app, "Magnetometer µT", grid=[0, 9])
    IMU_title_acc = Text(app, "Accelerometer Gs", grid=[0, 10])
    IMU_title_gyro = Text(app, "Gyroscope rad/s", grid=[0, 11])

    IMU_mag_x_text = Text(app, "xx", grid=[1, 9])
    IMU_mag_y_text = Text(app, "xx", grid=[2, 9])
    IMU_mag_z_text = Text(app, "xx", grid=[3, 9])
    
    IMU_acc_x_text = Text(app, "xx", grid=[1, 10])
    IMU_acc_y_text = Text(app, "xx", grid=[2, 10])
    IMU_acc_z_text = Text(app, "xx", grid=[3, 10])
    
    IMU_gyro_x_text = Text(app, "xx", grid=[1, 11])
    IMU_gyro_y_text = Text(app, "xx", grid=[2, 11])
    IMU_gyro_z_text = Text(app, "xx", grid=[3, 11])
    
    IMU_title_compass = Text(app, "Compass North Bearing", grid=[0, 13])
    IMU_compass_text = Text(app, "xx", grid=[1, 13])
    
    # end of objects
    
    
    app.repeat(1000, update_environment_sensors)
    app.repeat(100, update_IMU_sensors)
    
    app.display()


User avatar
bensimmo
Posts: 2617
Joined: Sun Dec 28, 2014 3:02 pm
Location: East Yorkshire

Re: GuiZero & the SenseHAT

Sun Mar 04, 2018 7:45 am

Ass of V0.45 RGB in Guizero has been implemented as has width, height properties to the text box etc.
https://lawsie.github.io/guizero/colors/

So you can now use that directly. :-)

Do keep an eye on https://lawsie.github.io/guizero/changelog/ or Github if you are using guizero, you may be able to do new things now :-)

User avatar
bensimmo
Posts: 2617
Joined: Sun Dec 28, 2014 3:02 pm
Location: East Yorkshire

Re: GuiZero & the SenseHAT

Tue Mar 06, 2018 9:40 am

guizero 0.45
----
height isn't on textbox and no multiline yet (must test Scotty101 alteration)

having trouble with RGB so still have to use built in colours..
(only spent 10 mins this morning)

the set_pixel part is easy
but get_pixel isn't reporting RGB list (though wouldn't tuple be better here than a list ?)
anyway.
just typing to remind myself later to hit the code and see what's happening/to report back for the next version.

Code: Select all

def change_pixel(x,y):    
    if big_waffle.get_pixel(x,y)== "white": #(255,255,255) does not work or variants of 
        big_waffle.set_pixel(x, y, (Red_slider.value, Green_slider.value, Blue_slider.value))
        sense.set_pixel(x,y, [Red_slider.value, Green_slider.value, Blue_slider.value])
    else:

scotty101
Posts: 2798
Joined: Fri Jun 08, 2012 6:03 pm

Re: GuiZero & the SenseHAT

Tue Mar 06, 2018 11:27 am

The get_color/set_color functionality is a bit unclear to me as to what it should do.

Consider the following example

Code: Select all

from guizero import App, Box, Waffle, PushButton

def getColor():
    print(grid.get_all())
    print(grid.get_pixel(0,0))
    print(grid.get_pixel(1,2))
    print(grid.get_pixel(2,2))
    
app = App("Changing size")

grid = Waffle(app, width=4, height=4)
btnGetColor = PushButton(app, text="Get Colors", grid=[0,0], command=getColor)

grid.set_pixel(0,0, (255,0,255))
grid.set_pixel(1,2, "#ffee11")
grid.set_pixel(2,2, "purple")

app.display()
Pressing the "Get Colors" button returns

Code: Select all

[['#ff00ff', 'white', 'white', 'white'],
 ['white', 'white', 'white', 'white'],
 ['white', '#ffee11', 'purple', 'white'],
 ['white', 'white', 'white', 'white']]
I've set the color of 3 different waffle cells using 3 different methods; RGB, hex value and 'color name'. The waffle either returns the color name or the hex color code.

What do you believe the get_pixel or get_all should return under the following conditions?
1. If i set the pixel color using a colour name eg "red"
2. if i set the pixel color using a hex color code eg "#ffeeff"
3. if i set the pixel color using an RGB value e.g. (255,0,255) (Which is internally converted to a hex color code)
Electronic and Computer Engineer
Pi Interests: Home Automation, IOT, Python and Tkinter

scotty101
Posts: 2798
Joined: Fri Jun 08, 2012 6:03 pm

Re: GuiZero & the SenseHAT

Tue Mar 06, 2018 11:59 am

More a self reminder here but this may be useful.

There is a tkinter method to get the RGB color from the named colors. I've modified the above code to convert both hex color codes and named colors to RGB values.

For the entire Waffle, it generates an RGB tuple for each cell in each row.
For an individual cell, it just returns an RGB tuple.

Code: Select all

from guizero import App, Box, Waffle, PushButton

def getColor():
    entireGrid = grid.get_all()
    rgbGrid = [[getRGBColor(color) for color in row] for row in entireGrid]
    
    print(rgbGrid)
    print(getRGBColor(grid.get_pixel(0,0)))
    print(getRGBColor(grid.get_pixel(1,2)))
    print(getRGBColor(grid.get_pixel(2,2)))


def getRGBColor(color):
    if isinstance(color,str):
        if color[0] == '#':
            hex_colors = (color[1:3], color[3:5], color[5:7])
            rgb_colors = tuple([int(hex_color,16) for hex_color in hex_colors])
        else:
            rgb = app.tk.winfo_rgb(color)
            rgb_colors = tuple([color//256 for color in rgb])
    else:
        rgb_colors = color
    return rgb_colors
        

    

app = App("Changing size")

grid = Waffle(app, width=4, height=4)
btnGetColor = PushButton(app, text="Get Colors", grid=[0,0], command=getColor)

grid.set_pixel(0,0, (255,0,255))
grid.set_pixel(1,2, "#ffee11")
grid.set_pixel(2,2, "purple")

app.display()

Electronic and Computer Engineer
Pi Interests: Home Automation, IOT, Python and Tkinter

User avatar
bensimmo
Posts: 2617
Joined: Sun Dec 28, 2014 3:02 pm
Location: East Yorkshire

Re: GuiZero & the SenseHAT

Tue Mar 06, 2018 1:12 pm

scotty101 wrote:
Tue Mar 06, 2018 11:27 am
.

What do you believe the get_pixel or get_all should return under the following conditions?
1. If i set the pixel color using a colour name eg "red"
2. if i set the pixel color using a hex color code eg "#ffeeff"
3. if i set the pixel color using an RGB value e.g. (255,0,255) (Which is internally converted to a hex color code)
1. "red"
2. #ffeeff
3. (255,0,255)

Unless I ask it with a .asHex() .asRGB() .asColourName()
Sort of thing.

I see your Github post :-).
Work in progress and it's nice to use though. Might have another play and try something else see what other problems I can find as a user.
Or nice to haves.
Still I've managed not to look at any Tkinter docs so far.

User avatar
bensimmo
Posts: 2617
Joined: Sun Dec 28, 2014 3:02 pm
Location: East Yorkshire

Re: GuiZero & the SenseHAT

Tue May 08, 2018 7:37 pm

GuiZero 0.5.0 (released April 2018)
bring Scotty's multiple line addition :-)
Not altered the examples above as I've forgotten my forum password to cut&paste it. But it works.


I notice he's missed off height in the docs as a property of it, but does mention it in the example and that works too.
Do you know what the width & height integer number refers to because a 20x20 box is not square?


Also I have a new replacement SenseHAT hot off the line as they had been waiting for them since one of my old ones had and led died on it.
These new one has a much squarer LED set on them and from first blinding look are brighter to. (They are not diffused like the round one)
Perhaps an LED 'waffle' shape change option in the GUI.
Attachments
IMG_20180508_204229-1248x628.jpg
IMG_20180508_204229-1248x628.jpg (132.19 KiB) Viewed 332 times

scotty101
Posts: 2798
Joined: Fri Jun 08, 2012 6:03 pm

Re: GuiZero & the SenseHAT

Tue May 08, 2018 10:16 pm

Not my textbox, just based on my hack and converted to something much more consistent with the rest of guizero.

Tkinter is weird, some widgets are sized by number of pixels and others and by number of characters. A 20x20 text box won't be square since characters are taller than they are wide.
Electronic and Computer Engineer
Pi Interests: Home Automation, IOT, Python and Tkinter

User avatar
bensimmo
Posts: 2617
Joined: Sun Dec 28, 2014 3:02 pm
Location: East Yorkshire

Re: GuiZero & the SenseHAT

Wed May 09, 2018 6:46 am

scotty101 wrote:
Tue May 08, 2018 10:16 pm
Not my textbox, just based on my hack and converted to something much more consistent with the rest of guizero.

Tkinter is weird, some widgets are sized by number of pixels and others and by number of characters. A 20x20 text box won't be square since characters are taller than they are wide.
It was referenced as yours ;-)

Might be having a play with the new windows and see if layout is easy, grids are quite rigid.
Really wish there was a Visual Python, take the chore out of it.

scotty101
Posts: 2798
Joined: Fri Jun 08, 2012 6:03 pm

Re: GuiZero & the SenseHAT

Wed May 09, 2018 11:14 am

bensimmo wrote:
Wed May 09, 2018 6:46 am
Really wish there was a Visual Python, take the chore out of it.
If you use PyQt or PySide instead of tkinter then you can use QT Designer to have a drag and drop UI creation tool but I agree, there is nothing quite like the simplicity of visual studio.
Electronic and Computer Engineer
Pi Interests: Home Automation, IOT, Python and Tkinter

Return to “Teaching and learning resources”

Who is online

Users browsing this forum: No registered users and 1 guest