The Display Text action

The Display Text action displays an arbitrary text on the LCD or VFD display. It acquires the display first, if necessary (see action Acquire Display action for details).

The action supports four different modes, depending on the use case:

Use case Example Mode Method
1 Display static text After system startup or resume, you want to display "Welcome to my home cinema!" Simple, static text mode Configure Display Text action through the GUI configuration dialog
2Display dynamic text with variablesYou store the current volume in a variable eg.globals.Volume. Your display shall display this value, even when it changes.Simple, variable text modeConfigure Display Text action through the GUI configuration dialog, passing variables.
3 Display dynamic text, event triggered Your favorite music player creates EG events (for example 'PlaybackStart' and 'PlaybackStop') when it starts/stops playing a song. You want to display the current song title or album info on the VFD or LCD screen. Script mode Call the DisplayText action in a EG Python Script action and pass it variables for the text to display.
4 Display dynamic text, periodically updated, not event triggered You want to display dynamic information which requires periodic updates and for which you don't get events in EG, like a clock, weather info, CPU temperature, e-mail status etc. User callback mode In an EG Python Script, create a user callback function and pass it to the  DisplayText action. DisplayText calls this function periodically.

1. Simple, Static Text Mode

The simple, static text mode is what is provided through this GUI dialog: You can display an arbitrary, static text on the iMON VFD or LCD display by calling this action, 'out of the box'. There are a lot of nice options to control the scroll mode, scroll speed and auto clear. Just play around with them to learn how they work.

2. Simple, Variable Text Mode

The simple, variable text mode is still very easy to use but you'll be surprised how powerful it is.

Suppose you store the current volume of your player in a global variable eg.globals.Volume. Now you intend to display the value of this variable, e.g. "Volume 55" on the display and even when this value changes, the new value shall be displayed, without calling the Display Text action again and again. This can still be done very easy using the GUI dialog, just following a special syntax: Enter
    Volume {eg.globals.Volume}
in the text field of the GUI dialog. The Display Text action parses the entered text continuously; if it finds text blocks following the pattern {valid_python_expression} it replaces the block with its evaluated value. This replacement (aka 'evaluation') is done continuously; at any time the value changes, the new value is shown on the display.

You could also display the payload of the last event using the variable eg.event.payload, but it's not advisable, because the next event overwrites this value again. And since Display Text evaluates the variable continuously, it would probably not display what you want. If you want to display the payload of an event, it's better to assign the value to your own global variable first. In the event handler macro of that event, just create a Python Command eg.globals.MyVariable = eg.event.payload and call Display Text with the expression {eg.globals.MyVariable}.

Display Priorities

Before we discuss the script mode, let's have a look at Display Priorities, another concept of this plugin. They are easy to understand. 

Imagine a stack of written papers. You can only see and read the top sheet of this stack, unless you remove it, then you see the next lower one. The 'Display Text' action works the same: It can handle an arbitrary number of display texts, but only the one with the highest display priority (i.e. the one with the lowest number) is actually displayed. As soon as it gets cleared, the next message with the next lower priority is being displayed. 

You can add (and clear) messages at any time with any priority. If a new message has top priority, it is immediately displayed, if it has a lower priority, it waits until the higher priority messages are cleared. Just if you add two messages with the same priority (let's say you add msg1 with prio=3 and msg2 with prio=3), the latter one replaces the first one. 

In a real-life configuration, you could use display priorities as follows:

Priority Usage Meaning
1 Keypress Display the name of the pressed key of the remote control during 1 second, then let it disappear again (see autoclear options)
3 DVBViewer Recordings While DVBViewer is recording, display recording information (channel, show, duration etc.) on the display
5 DVBViewer Playback or Live TV During normal live TV or playback of recordings information (channel, show, duration etc.) is shown.
20 Clock If nothing else happens, display a date-time clock on the display.

3. Script Mode

For most real-life situations the static text mode is not sufficient, because you want to display dynamic, real-life information on the display, like the song title or album information of what your favorite player currently plays or the status of an ongoing DVB recording. Sometimes such requirements can be achieved by the previously discussed dynamic text mode, but not always. In such cases the script mode comes into play.

One thing has to be said in advance: you need little Python knowledge to achieve this, or at least you are not frightened to learn it ;) It's not as difficult as it sounds in this documentation (it's my flaw to explain things too complicate ;))

OK, let's start. At the beginning, you need an EG event, like 'Play' or 'StartRecording' or similar. Such events are generated by other EventGhost Plugins like the VLC plugin or the DVBViewer plugin.

Triggered by such an event, you call an EventGhost Python Script, gather all information you want to display and call the DisplayText action with that information.

Here's an example of displaying information about what is currently shown in DVBViewer:

# Basic initialization
line1, line2 = '', ''

# Step 1: Get the data from DVBViewer and format it for the iMON display
data = eg.plugins.DVBViewer.GetCurrentShowDetails()
if data is not None and len(data) > 0:
line1 = '[' + data['channel'] + '] ' + data['title']
line2 = data['starttime'] + ' - ' + data['endtime']

# Step 2: Display the data on the screen
eg.plugins.iMON_Display.DisplayText(
msgPriority=5,
line1=line1,
line2=line2
)
Discussion

4. User Callback Mode

In the previous chapter we saw how to display dynamic text in the script mode, initially started by an event. But what if you don't have an event as starting point but your information is dynamically changing over the time and requires periodic updates? What if you want to display a clock (along with other information)? Maybe you could achieve that somehow with the script mode - but your configuration would probably end in a complicated monster.

With the user callback mode there exists a much more elegant way. The rest of this chapter focuses on this mode. 

User Callback Functions
Suppose you want to show a simple clock on the display in the format "HH:MM:SS". This text changes every second. Without a callback you would have to call the Display Text action every second from your configuration script. Very cumbersome and inelegant. User callbacks as provided by the Display Text action open a much more elegant way to achieve this. All you have to do is:
  1. To write a Python script function returning the text to display ("15:43:28", "15:43:29", "15:43:30", ...) and
  2. To tell the plugin to call this function every second (or in any other period).

That's the trick: "Don't call us, we call you" ;) With this powerful concept you're able to bring almost every desired text on the screen.

Here's the minimal code you have to put into an EG Python Script action: 

from time import localtime, strftime

# Step 1: Define the callback function. (Reduced to the bare minimum for this example)
# The callback function must return two strings (they can be empty), regardless of LCD or VFD display.
def MyClockCallback( displayType, msgPriority, userCallbackObj ):
line1 = strftime( "%H:%M:%S", localtime() )
line2 = ''
print "MyClockCallback called. Going to display '" + line1 + "'" # just for debug purposes. You can safely delete this line
return line1, line2

# Step 2: Call the 'DisplayText' plugin action
# Pass the previously defined callback function.
# The callback function will be called every 'userCallbackFreqInSec' seconds.
eg.plugins.iMON_Display.DisplayText(
userCallbackFunc=MyClockCallback, # that's the trick: pass the callback into the action
userCallbackFreqInSec=1.0, # how often the callback is called
)

Run this action script once... and your iMON display starts displaying a clock, second by second... :)

One could even more shrink this code (I didn't do it to keep it better readable). With not more than four (effective) lines of code you have already a running clock on your iMON display.

Let's dive a bit deeper into the details...

User callback function format
Your callback function must have the following format (or interface):
def UniqueCallbackFunctionName( displayType, msgPriority, userCallbackObj ):
return str( line1 ), str( line2 )
Name Type Description
UniqueCallbackFunctionName Function The name of the callback function is arbitrary - as long as it is unique within your configuration!
displayType String Input parameter; indicates the current display type. It is either 'LCD' or 'VFD'.
msgPriority Integer Input parameter; indicates the message priority.
userCallbackObj Object In-Out parameter; an optional, arbitrary object for user data, will be passed around with every callback function call. It allows the programmer to cache data and to get it back with the next cycle.

The userCallbackObj must be defined and passed into the DisplayText action; see description of DisplayText() parameters below for details.

return str( line1 ), str( line2 ) String, String The function must return two strings, regardless if VFD or LCD display. On VFD displays, they represent the two display lines. On LCD displays, the two strings are simply concatenated. The strings can be empty, of course.

The DisplayText action and its parameters

When working in script mode or user callback mode, you have to call the DisplayText action directly from a script. This section describes the parameters of the action.

Notes 

Name Type Default Description
msgPriority Positive integer 100 The display priority of the message.

The rules are:

  • Only the text with the highest priority is displayed, it hides all other texts with lower priorities.
  • As soon as 'ClearText' on the text with highest priority is called, the text with next lower priority is displayed.
  • Two texts with same priority can't exist together, the later one replaces the earlier one.
line1, line2 String (empty String) The upper and lower line on a VFD screen.

Or the beginning and end of display text on a LCD screen.

Supports variables and expressions with the following syntax: {valid_python_expression}

scrollMode Positive integer 1 Controls how the text is scrolled over the screen.

Supported values:

  • scrollMode = 0 - SCROLL_MODE_NO_SCROLL:
    Don't scroll. The text is clipped if it exceeds the display width.
  • scrollMode = 1 - SCROLL_MODE_ENDLESS_LOOP:
    Scroll continously in an endless loop.
  • scrollMode = 2 - SCROLL_MODE_STOP_SCROLL_STOP:
    Wait at the beginning, scroll till the end of the text, wait again, then start over.
scrollSpeed Positive decimal number 8.0 chars/sec Defines the scroll speed in characters per second
scrollWaitSec Positive decimal number 1.0 sec Only applies for scrollMode = 2 - SCROLL_MODE_STOP_SCROLL_STOP:
Defines the wait time in seconds at the beginning and at the end.
maxScrollLoops Positive integer or -1 -1 maxScrollLoops > 0:
The number of scroll loops to be performed. After that the message stands still.

maxScrollLoops = -1:
The feature is deactivated.

autoClearAfterSec Positive decimal number or -1 -1 autoClearAfterSec > 0:
Defines the autoClear countdown time. After this time, the message is automatically cleared.

autoClearAfterSec = -1:
The feature is deactivated.

Mutually exclusive to 'autoClearAfterLoops'
autoClearTimeAbsolute Boolean True Only applies if autoClearAfterSec > 0.

True:
The autoClear countdown starts after calling the action.

False:
The autoClear countdown starts after the message is first displayed.

autoClearAfterLoops Positive integer or -1 -1 autoClearAfterLoops > 0:
Defines the number of scroll loops after which the message is automatically cleared.

autoClearAfterLoops = -1:
The feature is deactivated.

Mutually exclusive to 'autoClearAfterSec'

effects 0 or 1 0 Defines additional display effects.

effects = 0:
Effects turned off

effects = 1:
Characters fly from left to right into the display

(more effects to come maybe in a future release)

userCallbackFunc Function reference or None None Points to the user callback function (if any). See separate chapter for more details.
userCallbackFreqInSec Positive decimal number 60.0 sec Defines the frequency in seconds of the user callback calls (i.e. how often the user callback function is called).
userCallbackObj Object or None None An arbitrary data object which is passed into the callback function. Allows to implement a data cache between callback calls etc.

In most cases a Python data dictionnary is the most convenient object type.

See below for an example on how to define and use it.

Tired of all this theoretical stuff? - OK, let's practice again, let's demonstrate a full-fledged callback example!

This example makes use of the DVBViewer plugin - however, it's just an example on how to get data from an external source in order to display it on the screen.

from time import time, strftime
from datetime import datetime as dt

# Step 0 (optional): Display priorities have been defined outside this script like this:
eg.globals.DISP_PRIO_RECORDINGS = 3

# Step 1: Define the callback function.
def MyRecCallback(displayType, msgPriority, userCallbackObj):
line1, line2, remaining = '', '', ''
try:
if msgPriority == eg.globals.DISP_PRIO_RECORDINGS:
# get attributes from the user callback object as passed
data = userCallbackObj['lastdata']
lastcall = userCallbackObj['lastcall']
now = time()

if (now > lastcall + 30.0 or data is None):
# Optimization: an expensive function is only called twice a minute
data = eg.plugins.DVBViewer.GetTimerDetails(
active=True,
enabled=True,
allRecordings=True,
enableDVBViewer = eg.plugins.DVBViewer.IsConnected(),
enableDVBService = True,
updateDVBService = False
)
#print data

# intermediate store for some user data, we get it back again with the next callback
userCallbackObj['lastcall'] = now
userCallbackObj['lastdata'] = data

if data is not None:
success = data[0]
if success and len(data[1]) > 0:
timerlist = data[1]
line1, i = '', 0
for timer in timerlist:
if i > 0:
line1 += ' '
i += 1
line1 += '[REC] ' + timer['description'] + ' (' + timer['channelName'] + ') ' + timer['startTime'] + ' - ' + timer['endTime']
endDateTime = dt.fromtimestamp( float( timer['endDateTime'] ) )
delta = endDateTime - dt.now()
totalSecs = delta.days * 24 * 60 * 60 + delta.seconds
hh, remainder = divmod( int( totalSecs ), 3600 )
mm, ss = divmod( remainder, 60 )
d = { 'H': hh, 'M': mm, 'S': ss }
remaining = '-%(H)02d:%(M)02d' % d # :)
currTimeStr = strftime("%H:%M:%S")
line2 = currTimeStr + ' ' + remaining
print 'MyRecCallback', line1, line2
except Exception, exc:
print unicode(exc)
return line1, line2


if True or eg.plugins.DVBViewer.IsRecording(
enableDVBViewer = eg.plugins.DVBViewer.IsConnected(),
enableDVBService = True,
updateDVBService = False,
):
# Step 2a (optional): Define a user data callback object
myCallbackObj = { 'lastcall': -1, 'lastdata': None }

# Step 2b (optional): Prefetch the data in order to start with a fully initialized screen
# Prefetching makes changes between messages smoother.
line1, line2 = MyRecCallback( 'None', eg.globals.DISP_PRIO_RECORDINGS, myCallbackObj )

# Step 3: Call the 'DisplayText' action
# See the 'Simple Clock' example for details
eg.plugins.iMON_Display.DisplayText(
msgPriority=eg.globals.DISP_PRIO_RECORDINGS,
line1=line1,
line2=line2,
scrollSpeed=8.0, # chars per second
scrollMode=2, # mode STOP_SCROLL_STOP
scrollWaitSec=1.0, # standstill time at the beginning and the end
maxScrollLoops=-1, # loop infinite
userCallbackFunc=MyRecCallback, # That's the trick! The callback function is passed into the action.
userCallbackObj=myCallbackObj, # You can pass any object back into the callback function
userCallbackFreqInSec=1.0 # How frequent the callback function is called
)
else:
eg.plugins.iMON_Display.ClearText(msgPriority=eg.globals.DISP_PRIO_RECORDINGS)
Discussion