Help with using roSGNode?

EDIT: My problem has changed slightly, but it still involves roSGNodes so I’ll just append this post with the issue I’m having. If anybody can help me I would very much appreciate it! I’m having trouble understanding how to add fields to the roSGNode when I create them, and how to make them the focused object on the screen. The following is my code with my best guess as to how it’s done. I would appreciate it if somebody could help me figure out what I’m doing wrong (when I attempt to create a node, I get an error in the console saying “Failed to create roSGNode with type Label”:

'live is an associative array with the following values:
'	start = time which the stream should start
'	end = predicted end time for the livestream
'	current = a roDateTime object to represent the system time (in UTC)

'stream is just an associative array with one member: stream
'	stream contains the fields needed to create the livestream,
'	when used as a field in VideoPlayer()

sub CountdownDisplay(live as object, stream as object)
	'create initial objects and show scene, so node can be created later	
	screen = CreateObject("roSGScreen")
	port = CreateObject("roMessagePort")
	screen.SetMessagePort(port)
	screen.Show()

	'obtain the time left for initial display of countdown timer
	secondsLeft = live.start.AsSeconds() - live.current.AsSeconds()

	'here I need to do something to create a visual countdown display
	countdown = CreateObject("roSGNode", "Label")     'because this fails to execute, the following three lines crash the program
	countdown.vertAlign = center                                
	countdown.horizAligin = center                             
	countdown.text = CountdownTime(secondsLeft)     'CountdownTime() returns a string with format hh:mm:ss

	while true
		msg = wait(1000, port)		'check for updates every second
		if msg = Invalid
			live.current.Mark()		'mark the time to ensure it's up to date

			secondsLeft = live.start.AsSeconds() - live.current.AsSeconds()		'update secondsLeft
			if secondsLeft > 0
				'here I need to modify the countdown to display an updated time
				countdown.text = CountdownTime(secondsLeft)
			else
				'assume wait time is over, get rid of the countdown screen, and go to the livestream
				screen.Close()
				VideoPlayer(stream)
			end if
		else if type(msg) = "roSGScreenEvent"
			if msg.IsScreenClosed()
				exit while
			end if
		end if
	end while

	return
end sub

I’m trying to use roSGNode to make a label in order to provide a user with some information, but my attempts to learn how to use roSGNode have been turning up stagnate. The documentation only tells me how to form the node, and I don’t know how to get all the data I need into the node object or how to properly display it. Below is my current attempt which failed.

	screen = CreateObject("roSGScreen")
	port = CreateObject("roMessagePort")
	screen.setMessagePort(port)

	'scene = screen.CreateScene("timer")
	screen.show()
	
	label = CreateObject("roSGNode", "Label")
	label.horizAlign = "center"
	label.vertAlign = "center"
	label.translation="[0,500]"
	label.width="1280"
	label.text = "there is no stream right now."

	label.setFocus()

Apparently you’re not supposed to use the node.field subtext even though the documentation suggests doing so. Could somebody help me straighten out what I’m supposed to do in this scenario?

EDIT: I tried investigating the documentation and used this code to see what would happen:

	screen = CreateObject("roSGScreen")
	port = CreateObject("roMessagePort")
	screen.setMessagePort(port)

	'scene = screen.CreateScene("timer")
	screen.show()
	
	label = CreateObject("roSGNode", "Label")
	aa={horizAlign: "center"
		vertAlign: "center"
		translation: "[0,500]"
		width: "1280"
		text: "there is no stream right now."
	}
	label.AddFields(aa)

	label.setFocus()

When I ran the program, it told me that it failed to create an roSGNode with type Label, even though it worked the first time. Am I missing something?

I’m not sure what you’re trying to do here.
Is this your /source/main.brs script?

It looks like you’re trying to create a screen without a scene.
That won’t work.

Can you confirm your project structure?

It should look something like this:
/manifest
/source/main.brs
/components/scenes/Main/Main.brs - All your scenes brightscript goes in here.
/components/scenes/Main/Main.xml - Calls Main.brs

Brightscript components are identified throughout the app via their defined name in the XML markup.

Here is how I think your code should look:
/components/scenes/Main/Main.xml


<component name="Main" extends="Scene">
  <script type="text/brightscript" uri="pkg:/components/scenes/Main/Main.brs" />
  <interface></interface>
  <children>
    <Label id="myLabel" />
  </children>
</component>

/components/scenes/Main/Main.brs

sub init()
  m.label = m.top.findNode("myLabel")
  m.label.horizAlign = "center"
  m.label.vertAlign = "center"
  m.label.translation="[0,500]"
  m.label.width="1280"
  m.label.text = "there is no stream right now."
end sub

/source/main.brs

sub main()
  m.screen = CreateObject("roSGScreen")
  m.port = CreateObject("roMessagePort")
  m.screen.setMessagePort(m.port)
  m.scene = screen.CreateScene("Main")
  m.screen.show()
end sub

Perhaps I should be more specific. (apologies, as I’d written this post in a momentary bout of frustration.) The function isn’t the main function of my program, but I assume the principle is the same. At least, I assume it is. Correct me if I’m wrong (please).

The text saying there is no stream is actually a placeholder, and my intent is to make a display informing the user of how long it will be before a stream takes place. I’ve been attempting another placeholder with a description on the main menu, a roListScreen, of the app I am building but that doesn’t seem to be working either (brightscript reeeeally doesn’t want me to tell time, I guess).

“scaper12123” wrote:
Perhaps I should be more specific. (apologies, as I’d written this post in a momentary bout of frustration.) The function isn’t the main function of my program, but I assume the principle is the same. At least, I assume it is. Correct me if I’m wrong (please).

The text saying there is no stream is actually a placeholder, and my intent is to make a display informing the user of how long it will be before a stream takes place. I’ve been attempting another placeholder with a description on the main menu, a roListScreen, of the app I am building but that doesn’t seem to be working either (brightscript reeeeally doesn’t want me to tell time, I guess).

If you can’t figure out how to tell the time using BrightScript, you’ll get totally wound up in knots trying to do anything using the Scene Graph API. And I don’t even know whether it’s even possible to mix Scene Graph API code (roSGNode) with standard Roku API code (roListScreen). As I told you in another thread, you’d be better off sticking with the standard components until you understand what you’re doing. It should be fairly easy to display a timer on an roListScreen; just over-write an existing field (Title, ShortDescriptionLine1, breadcrumb, etc.) with the timer value. If you’re stuck on that part, let me know and I’ll whip up a quick example.

“belltown” wrote:
If you can’t figure out how to tell the time using BrightScript, you’ll get totally wound up in knots trying to do anything using the Scene Graph API. And I don’t even know whether it’s even possible to mix Scene Graph API code (roSGNode) with standard Roku API code (roListScreen). As I told you in another thread, you’d be better off sticking with the standard components until you understand what you’re doing. It should be fairly easy to display a timer on an roListScreen; just over-write an existing field (Title, ShortDescriptionLine1, breadcrumb, etc.) with the timer value. If you’re stuck on that part, let me know and I’ll whip up a quick example.

I should stress that I am at least figuring out the basic components in roDateTime. Admittedly the possibilities of time-zone differences have made me lag a bit but that’s about the worst of it. What I was trying originally was to have a display with a countdown to the stream, coinciding with the timer, so that anybody watching would have something to look at when the stream is about to start, and so they wouldn’t have to keep re-selecting the livestream option on the main menu in the hopes of getting it when the stream is just starting.

I guess I should say I’m stuck on that part. Again, what I’ve technically been doing now is trying to print out a date in ShortDescriptionLine2 component along with some text, so it would say “next streaming date: [date goes here]”. I did attempt to do this by calling a function to return a string with the date but, unfortunately, the console gave me a syntax error.

“scaper12123” wrote:
what I’ve technically been doing now is trying to print out a date in ShortDescriptionLine2 component along with some text, so it would say “next streaming date: [date goes here]”. I did attempt to do this by calling a function to return a string with the date but, unfortunately, the console gave me a syntax error.
I see. Do you just want to display the date and/or time of when the stream will be available, or an actual continuously-updated timer that counts down to zero?

I want to ideally end up with the latter, which would be helpful in multiple ways. Again, it would allow the viewer to stay on the stream screen before the service, and it would allow me to check for the livestream immediately when the timer fires. Although if I had an example of both, I would really appreciate it. I’ve had issues on both fronts, after all.

Here’s an example showing both the date/time when the stream will play, as well as a countdown timer. All the elements you need should be there. It’ll be worth your while to try and understand the code, looking up stuff you don’t understand in the BrightScript Reference, or the Component Reference for roDateTime and roListScreen, then modify your own code accordingly. If you still don’t understand anything, then feel free to ask.


Sub Main ()
    ' Get the content list from somewhere
    contentList = getContentList ()

    ' Get the next event time somehow (e.g. 65 secs from now)
    dtNext = CreateObject ("roDateTime")
    dtNext.FromSeconds (dtNext.AsSeconds () + 65)

    ' Display countdown screen
    displayMainScreen (contentList, dtNext)
End Sub

Function displayMainScreen (contentList As Object, dtNext As Object) As Void
    ' Get the current GMT time
    dtNow = CreateObject ("roDateTime")

    ' Display content list using an roListScreen
    port = CreateObject ("roMessagePort")
    ui = CreateObject ("roListScreen")
    ui.SetMessagePort (port)
    ui.SetContent (contentList)
    ui.Show ()

    ' Keep track of which item is focused
    focusedIndex = 0
    While True
        ' Check for countdown completion every 500 milliseconds
        msg = Wait (500, port)
        ' Invalid is returned from Wait if a timeout occurs
        If msg = Invalid
            ' Update the current time
            dtNow.Mark ()
            ' Check how much time is left -- assumes all content items have same countdown time
            secondsLeft = dtNext.AsSeconds () - dtNow.AsSeconds ()
            ' Check if the countdown time has expired
            If secondsLeft > 0
                ' Countdown still running - modify content item with the time remaining
                contentItem = {}
                ' Keep the title the same
                contentItem.Title = contentList [focusedIndex].Title
                ' Modify ShortDescriptionLine 1/2 with the stream time and countdown timer
                contentItem.ShortDescriptionLine1 = "streaming at: " + formatDateTime (dtNext)
                contentItem.ShortDescriptionLine2 = "time left: " + formatTimeLeft (secondsLeft)
                ' Replace the focused content item with the modified item
                ui.SetItem (focusedIndex, contentItem)
            Else
                ' Coundown has expired - display original content item
                ui.SetItem (focusedIndex, contentList [focusedIndex])
            End If
        Else If Type (msg) = "roListScreenEvent"
            If msg.IsScreenClosed ()
                Exit While
            Else If msg.IsListItemFocused ()
                ' Keep track of which list item is focused
                focusedIndex = msg.GetIndex ()
            Else If msg.IsListItemSelected ()
                ' Play the selected video item
                displayVideoScreen (contentList [focusedIndex])
            End If
        End If
    End While
End Function

' Format a date time object: mm/dd/yy  hh:mm:ss
Function formatDateTime (dtGMT As Object) As String
    ' Convert GMT roDateTime to local
    ' Note - make a copy of dtGMT, to avoid multiple calls to ToLocalTime on same object
    dtLocal = CreateObject ("roDateTime")
    dtLocal.FromSeconds (dtGMT.AsSeconds ())
    dtLocal.ToLocalTime()
    h$ = Right ("0" + dtLocal.GetHours ().ToStr (), 2)
    m$ = Right ("0" + dtLocal.GetMinutes ().ToStr (), 2)
    s$ = Right ("0" + dtLocal.GetSeconds ().ToStr (), 2)
    Return dtLocal.AsDateString ("short-date") + "  " + h$ + ":" + m$ + ":" + s$
End Function

' Format the time left: "h:mm:ss"
Function formatTimeLeft (seconds As Integer) As String
    h% = seconds / 3600
    m% = (seconds - h% * 3600) / 60
    s% = seconds Mod 60
    Return h%.ToStr () + ":" + Right ("0" + m%.ToStr (), 2) + ":" + Right ("0" + s%.ToStr (), 2)
End Function

' Add code here to play the selected video content item
Function displayVideoScreen (contentItem As Object) As Void
    Stop
End Function

' Returns array of content meta-data items (hard-coded here, but could use XML, JSON, etc)
Function getContentList () As Object
    contentList = []
    contentList.Push({Title: "Title-1", ShortDescriptionLine1: "Desc-1a", ShortDescriptionLine2: "Desc-1b"})
    contentList.Push({Title: "Title-2", ShortDescriptionLine1: "Desc-2a", ShortDescriptionLine2: "Desc-2b"})
    contentList.Push({Title: "Title-3", ShortDescriptionLine1: "Desc-3a", ShortDescriptionLine2: "Desc-3b"})
    contentList.Push({Title: "Title-4", ShortDescriptionLine1: "Desc-4a", ShortDescriptionLine2: "Desc-4b"})
    Return contentList
End Function

EDIT: Display time using user’s local time zone.

“belltown” wrote:
Here’s an example showing both the date/time when the stream will play, as well as a countdown timer. All the elements you need should be there. It’ll be worth your while to try and understand the code, looking up stuff you don’t understand in the BrightScript Reference, or the Component Reference for roDateTime and roListScreen, then modify your own code accordingly. If you still don’t understand anything, then feel free to ask.

I’ll look through this and see if I can learn anything. Thank you for your help.

“scaper12123” wrote:
Admittedly the possibilities of time-zone differences have made me lag a bit but that’s about the worst of it.

When you asked about using AsSeconds in another thread, I said “assuming both roDateTimes are in GMT, this will work”. I emphasize again that you must operate in GMT. NEVER use local time for any time calculations – it will fail, due to the fact that local time is neither monotonic nor unambiguous (for example, the local time 1:30am occurs twice on the same day when Daylight Saving Time ends). The only thing local time should be used for is to display times to the user.

–Mark

“RokuMarkn” wrote:

“scaper12123” wrote:
Admittedly the possibilities of time-zone differences have made me lag a bit but that’s about the worst of it.

When you asked about using AsSeconds in another thread, I said “assuming both roDateTimes are in GMT, this will work”. I emphasize again that you must operate in GMT. NEVER use local time for any time calculations – it will fail, due to the fact that local time is neither monotonic nor unambiguous (for example, the local time 1:30am occurs twice on the same day when Daylight Saving Time ends). The only thing local time should be used for is to display times to the user.

–Mark
Good point. I’ve edited my example code above to display the time using the user’s local time zone. As Mark said, make sure the time calculations are done using GMT time. If your server returns time in a different time zone, then convert from that time zone to GMT before doing any further calculations on the time.

“belltown” wrote:
Good point. I’ve edited my example code above to display the time using the user’s local time zone. As Mark said, make sure the time calculations are done using GMT time. If your server returns time in a different time zone, then convert from that time zone to GMT before doing any further calculations on the time.

Hi there! This is me, replying a few days later. I took a good hard look at your code and I was able to get a description of streaming time in the description categories of the roListScreen nodes, so that was exciting! I am still struggling with the countdown timer I’m trying to make, however. I did realize what my problem was: I was misunderstanding the use of the Timer node in roSGNode. I am instead trying to make a Label node to display time in an hh:mm:ss format. My strategy is to update the label every second and change the time. If there is a more efficient way of making a visual countdown timer, please let me know.

In the meanwhile, I’m having trouble understanding how to add fields to the roSGNode when I create them, and how to make them the focused object on the screen. The following is my code with my best guess as to how it’s done. I would appreciate it if somebody could help me figure out what I’m doing wrong (when I attempt to create a node, I get an error in the console saying "Failed to create roSGNode with type Label):

'live is an associative array with the following values:
'	start = time which the stream should start
'	end = predicted end time for the livestream
'	current = a roDateTime object to represent the system time (in UTC)

'stream is just an associative array with one member: stream
'	stream contains the fields needed to create the livestream,
'	when used as a field in VideoPlayer()

sub CountdownDisplay(live as object, stream as object)
	'create initial objects and show scene, so node can be created later	
	screen = CreateObject("roSGScreen")
	port = CreateObject("roMessagePort")
	screen.SetMessagePort(port)
	screen.Show()

	'obtain the time left for initial display of countdown timer
	secondsLeft = live.start.AsSeconds() - live.current.AsSeconds()

	'here I need to do something to create a visual countdown display
	countdown = CreateObject("roSGNode", "Label")    'because this fails to execute, the next three lines crash the program
	countdown.vertAlign = center                                
	countdown.horizAligin = center                             
	countdown.text = CountdownTime(secondsLeft)     'CountdownTime() returns a string with format hh:mm:ss

	while true
		msg = wait(1000, port)		'check for updates every second
		if msg = Invalid
			live.current.Mark()		'mark the time to ensure it's up to date

			secondsLeft = live.start.AsSeconds() - live.current.AsSeconds()		'update secondsLeft
			if secondsLeft > 0
				'here I need to modify the countdown to display an updated time
				countdown.text = CountdownTime(secondsLeft)
			else
				'assume wait time is over, get rid of the countdown screen, and go to the livestream
				screen.Close()
				VideoPlayer(stream)
			end if
		else if type(msg) = "roSGScreenEvent"
			if msg.IsScreenClosed()
				exit while
			end if
		end if
	end while

	return
end sub