Protein is Good for You, part 2 (Matt Gertz)

Protein is Good for You, part 2 (Matt Gertz)

  • Comments 1

In yesterday’s blog post, I walked through an engine for translating DNA to its amino acid results via messenger RNA.  In today’s blog, we’ll work on the visualization using WPF StackPanels.  (This example requires VS2008, although it could certainly be written in WinForms as well with just a little bit of extra work.)

Caution:  The point of this blog is to demonstrate StackPanels, and specifically how you can control their orientation and nesting.  Consequently, for the sake of that argument, I’m creating a lot of text objects (one for each base and each amino) and nesting them to demonstrate this.  For a really robust program, I would ditch the extra objects and just do owner draw directly.  In other words, don’t use this example on enormous 20000-sequence strands of DNA, as neither you nor your PC would like the performance results.

StackPanels

In the last post, I added a ScrollViewer to the Window, changed it to scroll horizontally instead of vertically, but otherwise left it alone.  Now we’ll dig into the details of how we’ll populate it with StackPanels. 

StackPanels expose a very easy way to organize a set of drawable objects on the screen.  StackPanels expose a Children collection, and when you add objects to the that collection, they’ll display in the order you add them and in the direction you specify.  What’s really cool is that StackPanels will nest with each other, and I’ll take advantage of that fact by creating three StackPanels that organize information horizontally (one each for DNA, mRNA, and aminos), and then adding them to a parent StackPanel so that they line up with each other.  I’ll then add the parent StackPanel to the ScrollViewer so that the alignment of all three sub-panels can be seen.

Each of the sub-panels will contain one of more text objects which either contain the abbreviation of a base, the abbreviation of an amino, or blank space (representing unused intron material).  Aminos correspond to three bases, and so those text objects need to be three times wider.  I’ll create some helper functions to return the appropriate text objects:

    Const baseWidth As Integer = 16

 

    Private Function DrawBase(ByVal base As Char) As TextBlock

        Dim baseTextBlock As New TextBlock

        Select Case base ' Switch for colors

            Case "C"

                baseTextBlock.Background = Brushes.LightSalmon

            Case "G"

                baseTextBlock.Background = Brushes.LightGoldenrodYellow

            Case "A"

                baseTextBlock.Background = Brushes.LightGreen

            Case "T"

                baseTextBlock.Background = Brushes.LightSkyBlue

            Case "U"

                baseTextBlock.Background = Brushes.LightSteelBlue

        End Select

 

        baseTextBlock.Inlines.Add(New Bold(New Run(base)))

        baseTextBlock.Height = 32

        baseTextBlock.Width = baseWidth

        baseTextBlock.TextAlignment = TextAlignment.Center

 

        Return baseTextBlock

    End Function

 

That last function will be used by both DNA and RNA, creating and returning a fixed block of an appropriate color (a different one for each base) 16 x 32 pixels with the base abbreviation (passed into the function) centered within it.  I've chosen light colors so that the dark text will show up against the background properly. 

For the amino chains, it’s pretty similar, except that each block is three times wider, to keep the amino lined up with the codon:

    Private Function DrawAmino(ByVal amino As String) As TextBlock

        Dim aminoTextBlock As New TextBlock

        Select Case amino ' Switch for colors

            Case "Phe"

                aminoTextBlock.Background = Brushes.AliceBlue

            Case "Leu"

                aminoTextBlock.Background = Brushes.Beige

            Case "Ser"

                aminoTextBlock.Background = Brushes.Cyan

            Case "Tyr"

                aminoTextBlock.Background = Brushes.LightSeaGreen

           ' (Etc… the other aminos omitted for brevity’s sake; see final code for full list.)

 

        End Select

        aminoTextBlock.Inlines.Add(New Bold(New Run(amino)))

        aminoTextBlock.Height = 32

        aminoTextBlock.Width = baseWidth * 3

        aminoTextBlock.TextAlignment = TextAlignment.Center

        Return aminoTextBlock

    End Function

And for empty space, I just pass in the number of spaces required and multiple it by the baseWidth:

    Private Function DrawNoMap(ByVal noMapSize As Integer) As TextBlock

        Dim noMapTextBlock As New TextBlock

        noMapTextBlock.Background = ScrollViewer1.Background

        noMapTextBlock.Height = 32

        noMapTextBlock.Width = baseWidth * noMapSize

        Return noMapTextBlock

    End Function

 

Now I have the tools; we can put them to good use.  First, let’s create & populate the DNA StackPanel:

    Private Function DrawBases(ByVal s As String) As StackPanel

        ' DNA goes here (horizontal orientation):

        Dim myStackPanel As New StackPanel

        myStackPanel.HorizontalAlignment = System.Windows.HorizontalAlignment.Left

        myStackPanel.VerticalAlignment = System.Windows.VerticalAlignment.Top

        myStackPanel.Orientation = Orientation.Horizontal

 

        'Add child elements to the parent StackPanel.

        For i As Integer = 0 To s.Length - 1

            myStackPanel.Children.Add(DrawBase(s(i)))

        Next

 

        Return myStackPanel

    End Function

 

This is a simple function that does three things:

(1)    Create a stack panel that has horizontal orientation, containing object that start from the top left.

(2)    Step through each base in the DNA or RNA and add the TextBlock for it created in DrawBase.

(3)    Return the completed StackPanel

I can use this for both DNA and mRNA just by passing in the appropriate string.  Aminos are a bit trickier because I’ve decided to save them as lists of strings rather than one long string (to make it easier to access individual proteins should I wish to expand this program in the future):

    Private Function DrawAminos() As StackPanel

        ' Aminos go here (horizontal orientation):

        Dim myStackPanelAminos As New StackPanel

        myStackPanelAminos.HorizontalAlignment = System.Windows.HorizontalAlignment.Left

        myStackPanelAminos.VerticalAlignment = System.Windows.VerticalAlignment.Top

        myStackPanelAminos.Orientation = Orientation.Horizontal

        'Add child elements to the parent StackPanel.

        For Each p In Proteins

            Dim i As Integer = 0

            Do While i < p.Length - 1

                If p(i) = " " Then

                    Dim noMapSize As Integer = 0

                    Do While i < p.Length AndAlso p(i) = " " ' Make sure we don't go over when lining things up

                        noMapSize += 1

                        i += 1 ' Skip piece of intron for spacing

                    Loop

                    myStackPanelAminos.Children.Add(DrawNoMap(noMapSize))

                Else

                    myStackPanelAminos.Children.Add(DrawAmino(p.Substring(i, 3)))

                    i += 3 ' Skip length of amino

                End If

            Loop

        Next

 

        Return myStackPanelAminos

    End Function

 

In this case, the panel is once again oriented horizontally from the top-left, but I have to (a) go through each string in the list looking for aminos to draw and (b) determine if there are spaces that need to take up room in the drawing but don’t contain actual amino content.  Sequences without a stop codon would always be the final sequence (because the engine would have run through the whole string looking for them) and are just ignored by the application; they imply space without having to explicitly draw that space, since nothing else comes after it. 

I don’t want to create a text block for each space in an adjacent block of spaces – that’s too wasteful even for a simple example like this – so I’ll determine the number of adjacent spaces whenever I encounter them and group them together in on big “no map” block.  The “gotcha” in the above code is that I need to make sure that I don’t go past the final character both when looking for aminos as well as when drilling through the spaces.

Now, having created all of the drawing code as well as the code that runs through the sequences and calls them, I can create a master function that kicks off the drawing.  First, I need to get rid of any old content in the ScrollViewer, tossing it into the garbage collector:

    Private Sub DrawResults()

        'Get rid of any old content

        Me.ScrollViewer1.Content = Nothing

 

Next, I’ll create the master stack panel.  Unlike the others, its orientation is vertical (which is the default, so I don’t need to explicitly state it), because I’m going to stack the other panels vertically from top-left:

        ' This stack panel will contian the other stack panels.

        ' It defaults to a vertical orientation

        Dim myStackPanel As New StackPanel

        myStackPanel.HorizontalAlignment = System.Windows.HorizontalAlignment.Left

        myStackPanel.VerticalAlignment = System.Windows.VerticalAlignment.Top

 

Now, I’ll use the previously defined helper functions to create and draw the sub-panels, and then add them to the parent StackPanel:

        Dim myStackPanelDNA As StackPanel = DrawBases(DNA)

        Dim myStackPanelRNA As StackPanel = DrawBases(RNA)

        Dim myStackPanelAminos As StackPanel = DrawAminos()

 

        myStackPanel.Children.Add(myStackPanelDNA)

        myStackPanel.Children.Add(myStackPanelRNA)

        myStackPanel.Children.Add(myStackPanelAminos)

 

Finally, I add the parent panel to the ScrollViewer:

        'Add the StackPanel as the lone Child of the Border

        Me.ScrollViewer1.Content = myStackPanel

 

    End Sub

I’m almost done now; there’s only one thing left to do.  I’ve got to call DrawResults() after the DNA is loaded and translated, so I’ll add this line of code to the end of the LoadSequenceBtn_Click handler:

       DrawResults()

And we’re done!  I can launch the application, load in a file, see the translation visually, and save the results.  For example, with a text file containing the string:

ACACGGGCGCGTACAAAAGATGAACTGGGGCCCCGCGCTACCGCCGCCACATTAAAAA

The output I save will look like:

ACACGGGCGCGTACAAAAGATGAACTGGGGCCCCGCGCTACCGCCGCCACATTAAAAA

UGUGCCCGCGCAUGUUUUCUACUUGACCCCGGGGCGCGAUGGCGGCGGUGUAAUUUUU

           MetPheSerThr               MetAlaAlaVal  

 

On the screen, it looks much prettier, and I can scroll left-to-right to see all of the results.

That’s it for this post.  The final code is posted at https://code.msdn.microsoft.com/Release/ProjectReleases.aspx?ProjectName=templeofvb&ReleaseId=1172 -- enjoy!

‘Til next time,

  --Matt-
Leave a Comment
  • Please add 5 and 3 and type the answer here:
  • Post