Proof of Concept: Displaying Multi-Line Text in Indigo

Posted on
Wed Apr 22, 2015 7:49 pm
DaveL17 offline
User avatar
Posts: 6742
Joined: Aug 20, 2013
Location: Chicago, IL, USA

Proof of Concept: Displaying Multi-Line Text in Indigo

I went back and forth as to whether to post this in the WUnderground forum or more broadly, but since my example is Wunderground Plugin-based, I decided to post it here.

Challenge:
Weather Underground provides relatively long text alerts, however, Indigo has no native facility to display multi-line text. Why let that stop us?

alert.png
Alert Dialog - Proof of Concept
alert.png (42.69 KiB) Viewed 9118 times

How I Did It:
Enter Pillow. Pillow is the "modern version" of the Python Image Library (PIL). It has the facilities we need to generate images like the one above. The output image has only a couple of components. The underlying image (the black box and the icon) which can be just about anything. The PIL modules let us load the background image, superimpose text onto the image itself, and export it to a file that we can display on a control page. The image above isn't a mock-up, it's the actual output of Pillow that I'm displaying on a control page (okay, it is a bit of a mock-up as we're not going through a heat advisory here thankfully.)

To accomplish this, you will need to install the Pillow Python package. The basic script is simple, and I've added a little bit of flair to select different background images depending on the type of alert, and to dress up the text a bit to make the display more attractive. Here's the script that generated the image above:
Code: Select all
#! /usr/bin/env python2.6
# -*- coding: utf-8 -*-

from PIL import Image, ImageDraw, ImageFont
import textwrap

alertType = indigo.devices[19099912].states["alertType1"]
textToWrite = indigo.devices[19099912].states["alertMessage1"]
imagePath = '/Library/Application Support/Perceptive Automation/Indigo 6/IndigoWebServer/images/controls/static/'

font = ImageFont.truetype("Arial.ttf",12)
textColor = ("#FFFFFF")
textWidth = 85
writeMargin = 30
writeSpacing = 16
writeSpot = 20

# Load the proper background image based on the alert type.
if alertType in ("FIR"):
   image = Image.open(imagePath + "alertFire.png")

elif alertType in ("SEW","WRN"):
   image = Image.open(imagePath + "alertLightning.png")

elif alertType in ("WIN"):
   image = Image.open(imagePath + "alertSnow.png")

elif alertType in ("FLO","HUR","WAT"):
   image = Image.open(imagePath + "alertWater.png")

elif alertType in ("HWW","TOR","WND","TOW"):
   image = Image.open(imagePath + "alertWind.png")

else:
   image = Image.open(imagePath + "alertBang.png")
   
textToWrite = textToWrite.replace(u'\u000A', ' ') #replaces [line feed] with [one space]
textToWrite = textToWrite.replace('   ', ' ') #replaces [three adjacent spaces] with [one space]
textToWrite = textToWrite.replace('  ', ' ') #replaces [two adjacent spaces] with [one space]

# Add the text to the image
draw = ImageDraw.Draw(image)
lines  = textwrap.wrap(textToWrite, textWidth)
for line in lines:
   draw.text((writeMargin, writeSpot), line, font=font, fill=textColor)
   writeSpot += writeSpacing

# Write the file.
image.save(imagePath + "alert.png")

The rest is easy. Just add a refreshing image URL to a control page in the usual way. The script can almost certainly be improved with additional functionality, and to make the font display more clearly (although it does look pretty clear on an iPad screen.) You could superimpose the text onto a completely transparent background and make it look like the text was written right on the control page. There are lots of options.

This might be something that I'll add into a future version of the plugin (no promises!) and I will eventually make the alert background images available in the WUnderground Plugin Icon Pack when I'm happy with them. Unfortunately, I can't commit to supporting this as a part of my work on the plugin--just thought I would share. If someone wants to take the concept and turn it into a plugin, be my guest! :D

The Pillow Package that I used can be found here:
https://www.macports.org/ports.php?by=library&substr=py26-pillow

The instructions for installation and use can be found here:
http://pillow.readthedocs.org/installation.html
http://pillow.readthedocs.org/reference/index.html

Cheers,
Dave

P.S. Of course, this method could be used to display just about any multi-line text in a similar fashion--it works on more than just the weather!

I came here to drink milk and kick ass....and I've just finished my milk.

[My Plugins] - [My Forums]

Posted on
Wed Apr 22, 2015 10:21 pm
RogueProeliator offline
User avatar
Posts: 2501
Joined: Nov 13, 2012
Location: Baton Rouge, LA

Re: Proof of Concept: Displaying Multi-Line Text in Indigo

Good post, Dave... you might help some less comfortable with the process by posting where you retrieved the Pillow install that ultimately worked for you.

Adam

Posted on
Thu Apr 23, 2015 3:30 am
DaveL17 offline
User avatar
Posts: 6742
Joined: Aug 20, 2013
Location: Chicago, IL, USA

Re: Proof of Concept: Displaying Multi-Line Text in Indigo

RogueProeliator wrote:
Good post, Dave... you might help some less comfortable with the process by posting where you retrieved the Pillow install that ultimately worked for you.

Adam

Thanks Adam! I've added that information to the OP.

Dave

I came here to drink milk and kick ass....and I've just finished my milk.

[My Plugins] - [My Forums]

Posted on
Thu Apr 23, 2015 6:47 am
roussell offline
User avatar
Posts: 1108
Joined: Aug 18, 2008
Location: Alabama

Re: Proof of Concept: Displaying Multi-Line Text in Indigo

Nice writeup Dave. As an alternative I do something similar with some command-line utilities. Specifically the built-in 'fold' command to wrap text (when needed) and then the 'convert' utility from the ImageMagick package (Available through MacPorts, Homebrew, or the ImageMagick website) to convert the text to a png and format as needed.



As an example, this bit of code:
Code: Select all
echo "I really wish Indigo supported multi-line text boxes so we didn't have to resort to hacks like this." | fold -sw 50 | convert -page 500x360+0+0 -font Helvetica -style Normal -background black -undercolor black -fill white -pointsize 22 text:- +repage -background white -flatten output.png

Produces this image:
output.png
output.png (5.71 KiB) Viewed 9048 times
Last edited by roussell on Wed Mar 30, 2016 7:39 am, edited 1 time in total.

Posted on
Thu Apr 23, 2015 12:54 pm
matt (support) offline
Site Admin
User avatar
Posts: 21411
Joined: Jan 27, 2003
Location: Texas

Re: Proof of Concept: Displaying Multi-Line Text in Indigo

Great hacks. And help illustrates how badly Indigo needs this functionality. :mrgreen:

Image

Posted on
Thu Apr 23, 2015 1:00 pm
DaveL17 offline
User avatar
Posts: 6742
Joined: Aug 20, 2013
Location: Chicago, IL, USA

Re: Proof of Concept: Displaying Multi-Line Text in Indigo

Thanks Terry. Your hack got me to thinking that I could read the length of the string and select a background image that approximates the size needed.

I like your method too by the way! :D

I came here to drink milk and kick ass....and I've just finished my milk.

[My Plugins] - [My Forums]

Posted on
Thu Apr 23, 2015 1:01 pm
DaveL17 offline
User avatar
Posts: 6742
Joined: Aug 20, 2013
Location: Chicago, IL, USA

Re: Proof of Concept: Displaying Multi-Line Text in Indigo

Thanks Matt.

We're nothing if not resourceful around here!

I came here to drink milk and kick ass....and I've just finished my milk.

[My Plugins] - [My Forums]

Posted on
Thu Apr 23, 2015 2:08 pm
roussell offline
User avatar
Posts: 1108
Joined: Aug 18, 2008
Location: Alabama

Re: Proof of Concept: Displaying Multi-Line Text in Indigo

LOL Matt, just a little good-natured ribbing. We know you two are working your butts off!!! I had an old boss that always used to say that computers & technology would be a great career path if it wasn't for the people.... :|

Terry

Posted on
Thu Apr 23, 2015 2:11 pm
roussell offline
User avatar
Posts: 1108
Joined: Aug 18, 2008
Location: Alabama

Re: Proof of Concept: Displaying Multi-Line Text in Indigo

Thanks Dave. Most of the time I produce a transparent png and just overlay the text image over a different background. The 'convert' command is very powerful. ImageMagick is definitely a recommended tool to have in the toolbox.

Terry

Posted on
Mon Apr 27, 2015 6:44 pm
DaveL17 offline
User avatar
Posts: 6742
Joined: Aug 20, 2013
Location: Chicago, IL, USA

Re: Proof of Concept: Displaying Multi-Line Text in Indigo

Another copy of my original code. This one generates a box on the fly that is roughly sized to fit the text that's being written. I haven't taken the time to make the sizing perfect (the adjustment factors could use better math) but it's workable.

This code:
Code: Select all
#! /usr/bin/env python2.6
# -*- coding: utf-8 -*-

"""
Could set the size of the background image based on the length of
the string. This would be in lieu of using the standard image
backgrounds.
"""

from PIL import Image, ImageDraw, ImageFont
import textwrap

textToWrite = u"Hello Professor Falken. Would you like to play a game?"

# Set PIL parameters.
imagePath = '/Library/Application Support/Perceptive Automation/Indigo 6/IndigoWebServer/images/controls/static/'
font = ImageFont.truetype("Arial.ttf",12)
boxColor = ("#000000") # Note that this can also be an RGB tuple (123,123,123) or string "black"
textColor = ("#FFFFFF")
textWidth = 85
writeMargin = 30
writeSpacing = 15
writeSpot = 15

# Take the string and prettify it.
textToWrite = textToWrite.replace(u'\u000A', ' ') #replaces [line feed] with [one space]
textToWrite = textToWrite.replace('   ', ' ') #replaces [three adjacent spaces] with [one space]
textToWrite = textToWrite.replace('  ', ' ') #replaces [two adjacent spaces] with [one space]

# Determine the "shape" of the output text.
lines = textwrap.wrap(textToWrite, textWidth)
linesNum = len(lines)

# Determine the shape of the output box. We need a box which is at least one line high, but
# otherwise sized to fit the text in question.

# Box width.
if int(textWidth) > len(textToWrite):
   boxWidth = int(len(textToWrite) * 8)
else:
   boxWidth = int(textWidth * 6.4)

if boxWidth <= 250:
   boxWidth = 250

# Box height.
boxHeight = (linesNum * 25)

if boxHeight <= 40:
   boxHeight = 45

# Draw the box.
image = Image.new('RGB', (boxWidth, boxHeight), (boxColor))
draw = ImageDraw.Draw(image)

# Write the text
for line in lines:
   draw.text((writeMargin, writeSpot), line, font=font, fill=textColor)
   writeSpot += writeSpacing

# Save the file.
image.save(imagePath + "alert.png")

Generates this image:
alert.png
alert.png (2.05 KiB) Viewed 8905 times

and this:
alert1.png
alert1.png (10.63 KiB) Viewed 8905 times

I came here to drink milk and kick ass....and I've just finished my milk.

[My Plugins] - [My Forums]

Posted on
Mon Apr 27, 2015 10:58 pm
Korey offline
User avatar
Posts: 811
Joined: Jun 04, 2008
Location: Henderson, NV

Re: Proof of Concept: Displaying Multi-Line Text in Indigo

matt (support) wrote:
Great hacks. And help illustrates how badly Indigo needs this functionality. :mrgreen:


Yes Please! :lol: Lets go Version 7!

:D :D :D

Seriously.. HUGE Thanks to you and Jay for the continued work you both do on this fantastic ecosystem!

--
Korey

Posted on
Mon Oct 19, 2015 6:02 am
Richard offline
Posts: 68
Joined: Feb 05, 2015

Re: Proof of Concept: Displaying Multi-Line Text in Indigo

I know it's old language but for 2-3 line text I used applescript with variables but also waiting to see if

Yes Please! :lol: Lets go Version 7!


has it.

Posted on
Fri Nov 13, 2015 10:36 pm
DaveL17 offline
User avatar
Posts: 6742
Joined: Aug 20, 2013
Location: Chicago, IL, USA

Re: Proof of Concept: Displaying Multi-Line Text in Indigo

Was tweaking some Gnuplot charts tonight and came up with yet another idea to display multi-line text. Creating a Gnuplot chart that has only one element--a label.

There are just two elements required for this approach. First, save the multi-line text to a plain text file, which includes a variable assignment (not an Indigo variable assignment.) This can be done any number of ways. You can include multiple variables in this file if you like. It looks like this:
Code: Select all
alert1_text = "...Winter Weather Advisory in effect from 6 PM CST /7 PM EST/\nthis evening to 6 am CST /7 am EST/ Tuesday...\n\nThe National Weather Service in Chicago has issued a Winter\nWeather Advisory for snow...which is in effect from 6 PM CST /7\nPM EST/ this evening to 6 am CST /7 am EST/ Tuesday. The Winter\nStorm Watch is no longer in effect.\n\n* Timing...Monday night through the predawn hours Tuesday.\n\n* Accumulations...3 to 6 inches with some areas possibly seeing\n over 6 inches.\n\n* Main impact...untreated roads will become snow covered \n resulting in difficult travel. Visibility may be reduced to \n around one quarter mile at times. \n\n* Other impacts...the dry nature of the snow and increasing \n winds early Tuesday may result in blowing and drifting of \n snow... particularly in open areas...even after snowfall has \n ended. \n\nPrecautionary/preparedness actions...\n\nA Winter Weather Advisory for snow means that periods of snow\nwill cause primarily travel difficulties. Be prepared for snow\ncovered roads and limited visibilities...and use caution while driving."
For this example, I've named the plain text file alert1.txt (it's based on a WUnderground alert, where carriage returns '\n' have been inserted in place of the code '\u000A'.) You can format your text to taste. Next, create a Gnuplot script based on the following format:
Code: Select all
#! /usr/bin/env gnuplot
# -*- coding: utf-8 -*-
reset
load "path_to_your_text_file/alert1.txt"

set terminal pngcairo enhanced background "#000000" size 420,400 truecolor
set output "/path_to_output_file/alert1.png"
unset xdata
unset ydata
unset xtics
unset ytics
unset key
unset timefmt
set xrange [0:1]
set yrange [0:1]
set title "Severe Weather Alert 1" font "Lato Bold,10" textcolor rgb "#FF0000"
set label alert1_text at graph 0.5,0.965 center font "Lato Light,9" textcolor rgb "#FFFFFF"
set border linetype rgb "#666666"
plot [0:1][0:1] NaN

These two files combine to produce this:
alert1.png
alert1.png (32.33 KiB) Viewed 8569 times

Obviously, more could be done to dress up the text, and you can use different colors and fonts to taste; but it gets the point across.

Dave

I came here to drink milk and kick ass....and I've just finished my milk.

[My Plugins] - [My Forums]

Posted on
Fri Nov 20, 2015 10:42 pm
DaveL17 offline
User avatar
Posts: 6742
Joined: Aug 20, 2013
Location: Chicago, IL, USA

Re: Proof of Concept: Displaying Multi-Line Text in Indigo

Had some time on a plane to tinker with the Gnuplot approach. Getting pretty close to a workable solution.

Python script to create the plain text file that holds cleaned up alert text:
Code: Select all
#! /usr/bin/env python2.6
# -*- coding: utf-8 -*-

import subprocess
import textwrap

### Settings ###
dev                     = indigo.devices[1899035475]
path_to_gnuplot_script  = '/opt/local/bin/gnuplot /Users/USERNAME/Dropbox/Indigo\ Scripts/weatherAlerts.gp'
text_file               = '/Users/USERNAME/Dropbox/Public/alertText.txt'
text_width              = 112

def cleanUpString(val):
    clean_list = [
        (' am ', ' AM '),
        (' pm ', ' PM '),
        ('*', ' '),
        ('\u000A', ' '),
        ('...', ' '),
        ('/ ', '/'),
        (' /', '/'),
        ('/', ' / '),
    ]

    for (old, new) in clean_list:
        val = val.replace(old, new)

    val = ' '.join(val.split())
    return val

def writeAlert(alert_num, alert, expires):
    output_text   = ""
   
    if len(alert) <= 1:
        if alert_num == 1:
            expiry = u"No alerts at this time."
        else:
            expiry = ""
    else:
        expiry = u"Expires: "

    text_to_write = cleanUpString(alert)
    text_to_write = u"alert%s_text = \"%s %s %s\"" % (alert_num, text_to_write, expiry, expires)
    text_to_write = textwrap.wrap(text_to_write, text_width)

    for element in text_to_write:
        output_text = "\n%s%s\\n\\\n" % (output_text, element)
    text_file.write(output_text[:(len(output_text)-4)])

try:
    alert_list = [
        (1, dev.states['alertMessage1'], dev.states['alertExpires1']),
        (2, dev.states['alertMessage2'], dev.states['alertExpires2']),
        (3, dev.states['alertMessage3'], dev.states['alertExpires3']),
        (4, dev.states['alertMessage4'], dev.states['alertExpires4']),
    ]

    text_file = open(text_file, 'w')

    for (alert_num, alert, expires) in alert_list:
        writeAlert(alert_num, alert, expires)

    text_file.close()

    proc     = subprocess.Popen(path_to_gnuplot_script, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True)
    out, err = proc.communicate()

    if len(err) >= 1:
        indigo.server.log(u"alertTextImages.py weather alerts images: error = %s" % err)
    else:
        indigo.server.log(u"alertTextImages.py weather alert images created successfully.")

except Exception, e:
    indigo.server.log(e)

Gnuplot script to produce the image file. This script is pretty tight, but will likely need some adjusting as different lengths of alerts filter through:
Code: Select all
#! /usr/bin/env gnuplot
# -*- coding: utf-8 -*-

reset

load "/Users/USERNAME/Dropbox/Public/alertText.txt"

output_path = "/Users/USERNAME/Dropbox/Public/"
title_name = "Severe Weather Alert "
x_offset_title = -31.15
y_offset_title = -0.75
x_offset_label = 0.01
y_offset_label = 0.90

set terminal pngcairo enhanced background "#000000" size 740,200 truecolor
set border linetype rgb "#666666"
set xrange [0:1]
set yrange [0:1]
unset key
unset timefmt
unset xdata
unset xtics
unset ydata
unset ytics

set output output_path."alert1.png"
set title title_name."1" offset x_offset_title,y_offset_title font "Lato Bold,10" textcolor rgb "#FF0000"
set label alert1_text at graph x_offset_label,y_offset_label left font "Lato Light,10" textcolor rgb "#FFFFFF"
plot [0:1][0:1] NaN

unset label
set output output_path."alert2.png"
set title title_name."2" offset x_offset_title,y_offset_title font "Lato Bold,10" textcolor rgb "#FF0000"
set label alert2_text at graph x_offset_label,y_offset_label left font "Lato Light,9" textcolor rgb "#FFFFFF"
plot [0:1][0:1] NaN

unset label
set output output_path."alert3.png"
set title title_name."3" offset x_offset_title,y_offset_title font "Lato Bold,10" textcolor rgb "#FF0000"
set label alert3_text at graph x_offset_label,y_offset_label left font "Lato Light,9" textcolor rgb "#FFFFFF"
plot [0:1][0:1] NaN

unset label
set output output_path."alert4.png"
set title title_name."4" offset x_offset_title,y_offset_title font "Lato Bold,10" textcolor rgb "#FF0000"
set label alert4_text at graph x_offset_label,y_offset_label left font "Lato Light,9" textcolor rgb "#FFFFFF"
plot [0:1][0:1] NaN

Final image based on the above:
alert1.png
alert1.png (23.85 KiB) Viewed 8506 times

I came here to drink milk and kick ass....and I've just finished my milk.

[My Plugins] - [My Forums]

Posted on
Sat Nov 21, 2015 9:39 am
DaveL17 offline
User avatar
Posts: 6742
Joined: Aug 20, 2013
Location: Chicago, IL, USA

Re: Proof of Concept: Displaying Multi-Line Text in Indigo

Posted an update to the Python script above to make it a bit tidier.
Dave

http://forums.indigodomo.com/viewtopic.php?f=149&t=13908&p=105878#p105878

I came here to drink milk and kick ass....and I've just finished my milk.

[My Plugins] - [My Forums]

Who is online

Users browsing this forum: No registered users and 3 guests