textport for performance | TouchDesiger

consoleText

I love a good challenge, and today on the TouchDesigner slack channel there was an interesting question about how you might go about getting the contents of the textport into a texture to display. That’s a great question, and I can imagine a circumstance where that might be a fun and interesting addition to a set. Sadly, I have no idea about how you might make that happen. I looked through the wiki a bit to see if there were any leads, and it’s difficult to see if there’s actually a good way to grab the contents of the textport.

What do we do then?!

Well, it just so happens that this might be another great place to look at how to take advantage of using extensions in TouchDesigner. Here our extension is going to do some double duty for us. The big picture idea is that we’ll want to be able to use a single call to either display a string, or print and display a string. If you only wanted to print it you could just use print(), so we’ll leave that one out of the mix for now.

Let’s take a look at the extension and then pull apart what’s happening in side.

'''
author | matthew ragan
web | matthewragan.com

The idea behind the display class comes from a question on the TouchDesiger 
slack channel about how to handle displaying text for performance. While you
might choose to display the console, the challenge here is that you'll end 
up with another window open on your output machine. Ideally, you only draw
a single openGL context - this results in the best possible performance
from TouchDesigner. With that in mind, how might you approach wanting to use
the textport in an artistic way for your project? 

This example tackles that challenge with a set of methods designed to 
print to a texture intended for display, as well as to the text port.
'''

class disp():

    def __init__( self ):
        ''' our init method does a bit of set-up and starting organization.

        We need a few things to be available to us in this method. It's handy
        to have the target table, the target text TOP, a message with a placeholder
        and some information for our start and end rows to be displayed.
        '''
        self.TextTarget = op( 'table_target' )
        self.DisplayText = op( 'text_for_display' )
        self.MessagePrompt = ">>> {msg}"
        self.StartRow = 0
        self.EndRow = 11
        self.TextTarget.clear()

        return

    def Display( self, inputMsg="hello world" ):
        ''' The display method will populate a table which is used to feed a text TOP

        The big idea here is that we need to fill in a table DAT that will then feed
        a text TOP. Why a table DAT instead of a text TOP? Well, you can do whatever you
        want. My idea here is that a table DAT is a little easier to think of in terms 
        of single rows that can fill up a text TOP. We can also do fancy things like use
        a select to make sure that our text is always displayed even if our start and end
        rows get away from us. A text DAT will just keep filling our text TOP, and
        we might end up with squashed text we can't see. 

        In this method we format our MessagePrompt with the supplied text. We also do a 
        quick check to see if our text will fit in the display. If we have more rows than
        can be displayed, we update our class variables to so that our last lines will
        remain visible.

        Finally, we return our formattedMsg - why? This is handy if we want to use another
        method to actually print our message to the textport as well.
        '''
        formattedMsg = self.MessagePrompt.format( msg = inputMsg )

        self.TextTarget.appendRow( [ formattedMsg ] )

        if self.TextTarget.numRows > 11:
            self.StartRow += 1
            self.EndRow += 1

        else:
            pass

        return formattedMsg

    def PrintAndDisplay( self, inputMsg="hello world" ):
        ''' The PrintAndDisplay method will both add the message to the text TOP, and 
        print the same message to the console.

        You may be wondering why we return the formatted message - the trick here is that
        we can take advantage of the work that arleady happens in the Display method.
        Why write that whole bit again, if we can just use the same logic and work 
        we've already done, and just add a final step of printing to the textport as well.
        '''

        forConsole = self.Display( inputMsg )
        print( forConsole )
        return

        def ClearDisplay( self ):
        ''' Clear display dumps out the contents of our target table.

        This method seems silly, but the idea is that you're likely to want
        a fast and clean way of cleaning out the contents of your display window.
        This method does only that simple little task. This will also reset our 
        start and end bounds to make sure that we're getting the correct selection
        for our text TOP.
        '''
        self.TextTarget.clear()
        self.__init__()
        return

Okay, so what exactly are we doing here?!

The big picture is that we want a way to be able to log something to a text  object that can be displayed. In this case I choose a table DAT. The reasoning here is that a table DAT before being converted to just a text DAT allows us to do some simple clean up and line adjustments. Each new entry is posted in a row – which makes for an easy way to limit the number of displayed rows. We can do this with a select DAT – which is where we use our StartRow and EndRow members.

Why exactly do we use these? Well, this helps ensure that we can keep our newest row displayed. A text TOP can accept a text DAT of any length, but at some point the text will spill off the bottom – unless you use adaptive sizing. The catch there is that at some point the text will become impossible to read. A top and bottom boundary ensures that we can always have something portion of our text displayed. We use a simple logical test in our Display() method to see if we’ve hit that boundary yet, and if we have we can update our members plus one… moving them both along at the same time.

You may also notice that we have a separate method to display and print… why not just do this in a single method. Well, that’s a great question. We could just use a single method for this with another argument. That’s probably a better way to tackle this challenge, but I wanted to use this opportunity to show how we might call another method from within our class. This can be helpful in a number of different situations, and while this application is a little too simple to really take advantage of that technique, it gives you a peak into how it might work.

Want to download the tox and take it for a test drive? You can find the source code here.