In my first tutorial, I introduce the Composed Systems' open source framework called the MVA Framework. We looked at how to port a common UI application to the MVA style of programming. This tutorial picks up where I left off and covers how to have more than one UI in an application.
The Main course: Our task for this meal...
Our last application was largely unremarkable on the surface, but under the hood we architected it to let us easily swap out one UI for another. What if my application needs more than one UI? How do I decompose a single UI into more modular UIs? In this tutorial I'm going to introduce a few more classes and concepts that'll allow us to host multiple "widgets" in a UI. We're going to make the UI you see below composed of two "widgets". One widget is given the responsibility of stopping the application by presenting a stop button to the user. The other widget simply has a ring control that presents options to a user.
Let's get Cooking
Using our same abstract graphics from the first tutorial, the goal is to create the following:
Here, the red class (IViewModel.lvclass) is a thing designed to host other things. In the first tutorial we hosted a UI (IViewable.lvclass, the purple class). Now I'm going to introduce a new concept: the view manager (IViewManager.lvclass; that's the green class above). Among its other responsibilities, the View Manager is responsible for defining the layout of the widgets. We'll talk about other responsibilities in later tutorials. Our two widgets are are both of type IViewable.lvclass (the purple and the yellow classes).
Organizing Your UI Layout- The View Manager
As mentioned above, one of the View Manager's responsibilities is to define the layout of a UI. Defining a layout means putting down one subPanle per widget. To create a View Manager:
1. create a new Actor Framework actor and inherit from IViewManager.lvclass:
"...\MVA Tutorials\2 - A Community of Two\gpm_packages\@cs\mva-framework\Source\Framework\IViewManager\IViewManager\IViewManager.lvclass"
2. Create an override of the Actor Core.
3. Place your sub panels on the front panel.
We need to make the MVA framework aware of these subpanels. To do that we call
"New Protected Subpanel.vi" (...\MVA Tutorials\2 - A Community of Two\gpm_packages\@cs\mva-framework\Source\Framework\IViewable\ProtectedSubpanel\New Protected Subpanel.vi).
Then we add the subpanel references to our Actor's private data
Creating the widgets follows same procedure described in the first tutorial. So now we have all the ingredients we need. We have a thing that hosts layouts (the red class, aka IViewmodel), a thing that defines the number and layout of widgets (the green class, aka IViewManager), and two widgets (the yellow and purple classes, aka IViewables).
A widget Handling Events
In our previous tutorial, the stop button value change event was conveyed out of the View (Purple class) to the ViewModel (red class) in the override of "catch nested updates". Then the view model stopped itself which caused the framework to shutdown all nested views.
What if we don't want to convey an event outside of a view for handling, but rather handle the event in the view itself? There are two concepts I'll need to introduce here: 1. The Framework API that allows us to register things for events, 2. The thing that handles the event.
In the actor core of the yellow actor you'll see a new VI called "Register For Control Events.vi".
Register for Control Events takes an array of control references and an array of event types as inputs. What we're telling the framework is "Hey MVA, when you see any of these controls do any of these events, then receive and act on the value change event." Note the array of control references and the array of events is NOT parallel. If the framework sees any of the events in the event array for any of the controls in the control reference array, it will act.
The thing doing the acting is an override of an IViewable called "Receive Value Change Event.vi". On an event, this VI will execute and looks like:
In this VI, we take the user's selection from the ring control and show it in a one button dialog. Think of this case structure to be similar to the frame of an even structure. But like our deconstructed hamburger from the first tutorial, we've cut up the even handler into more granular parts. Here's what I mean:
The block diagram on the left is our standard event structure set to handle a value change on control labeled "Choose An Option". It provides the control reference, old and new values, and time. Our MVA block diagram on the right does the exact same thing using APIs instead of the event structure.
Paging our Chef. Time to Plate our food!
Now we just need a chef to assemble our ingredients. And if you'll recall from the first tutorial, that's the responsibility of the Assembler. Below is our assembler for this application. Again, I've included pictures to help illustrate what's going on.
Something to note: we've introduce a new VI called "Write Startup Views". This framework VI does two things: 1. Indicates nesting, 2. Assigns a name to a widget (we'll see where the name is used shortly). "Write Startup Views.vi" embeds views into a View Manager. You'll notice our green class (view manager) is passed in as the top left input, and views and names are passed in as parallel arrays. We're directing the framework to insert the view classes into this specific view manager and then we give those views names.
We give the views names so the view manager knows which subpanel will host which view. Inserting the correct view into the desired subpanel happens in the "Initialize Front Panel" of our View manager, shown below:
In our override we use a for loop to scan through the array of names. In each case, we select the specific Protected subpanel reference (we created the subpanel references earlier in this tutorial) and pass in the enqueuer of the view to host. "Send Insert Front Panel" is the framework VI responsible for insert a view into a subpanel.
Done and Done! We now how a modular UI composed of two widgets.
So was it worth it? Well... now I can easily swap in any widget I want. But here's a cool bonus. Let's imagine you like the UI of the widget but not it's behavior. Since events are handled in an override called "Receive Value Change Events" all you would have to do to create new behavior is make a child View from the parent and override its "Receive Value Change Events" and call the child in the assembler. That's kinda cool!
To whet your appetite (aka preview for the next tutorial), our two widgets compose the UI, but we'll likely want them to communicate with each other. I'm going to borrow from one of my all time favorite TV shows "Star Trek" to demonstrate how we open hailing frequencies between the two views. Prepare to make it so!
- By Chris Cilino, Composed Systems Partner
I recently became a partner at Composed Systems in July 2019. I hope you'll join me in my journey as I come up to speed on our tools set. One tool I want to highlight is the MVA Framework. The MVA Framework accelerates our development and give us flexibility to rapidly respond to changing requirements. And it can do the same for you since Composed Systems has made the framework open sourced.
I've found that I learn best when I first personally succeed at a task, and then share that knowledge with others. So I'm blogging about my journey to master the MVA Framework.
All of the tutorials are available at the MVA-Tutorial Bitbucket Repository. Each tutorial is a LabVIEW project designed to present the smallest set of concepts possible to accomplish a meaningful task. And each tutorial builds on the concepts of the previous one. So DON'T SKIP!
To view any of the tutorials
1. Clone the repository using Sourcetree (or your favorite Git method).
2. Install GPackage manager.
3. Open GPackage manager and navigate to the directory containing the LabVIEW project for a given tutorial.
4. Install the dependencies:
5. Use this blog in conjunction with the comments on the block diagrams to go on the journey with me.
The longest journey begins with a single step, so what better way to start than by learning to stop? Wait for it.... here's what I mean. Whenever I learn a new framework or look at someone's code, my very first question is "How does it stop?" In learning how code stops, I find I learn the most fundamental notions of the code. So that's where I'll start with the MVA framework (hence the clever title "Start by Stopping").
In this first tutorial I'm sharing how I migrated the following application into an MVA application.
There's nothing terribly fantastic about this VI. It's designed to start, wait for the Boolean button value change, and then stop. The MVA framework allows us deconstruct the front panel and block diagram into more granular parts you can programmatically interact with. Deconstructed food is a good analogy here, mostly because I like food. You might be used to a hamburger that looks like
But the MVA Framework deconstructs it to look like
so you can eat any part independently of the other and in whatever order you'd like. Let's deconstruct our VI into is separate parts.
First we will split the front panel from the code by using subPanels, and we'll insert our UI into something designed to host UIs. I'm going to concentrate more on "What we are doing" and less on "Why" for the time being.
We'll make both the thing hosting the VI (aka the "hoster") and the hosted VI Actor Framework actors.
WAIT! Actor Framework?! Ruuunnnn!!!! Deep breaths...before panic sets in, I'm a noob with the Actor Framework (AF), so I'm with ya. There isn't much AF stuff you'll need to know to use the MVA Framework. You'll see... just keep with me.
To create the "hoster" MVA actor
So this is what we're putting together.
Next we cut up the block diagram and represent elements of the block diagram with VIs. The while loop becomes the actor core and, instead of the event handler, we introduce an API to Register For Control Events. The VI used to register for events is called "Register For Control Events.vi" and looks like
Path: gpm_packages\@cs\mva-framework\Source\Framework\ActorEvents\IEventAggregator\Register For Control Events.vi
We pass in an array of control references to dynamically register to an event handler (we'll get there). We also pass in an array of event types we want to handle. In this case we are saying "Something execute when the value of "stop" changes." In effect the code above is a programmatic version of:
Since we have two actors running (our hoster actor and hosted actor) we want our stop command to be handled by the hoster. The MVA framework propagates the "stop" to the hosted Actor. So we need to create an override in the hoster called "Catch Nested Value Update.vi". While each UI can (and often does) handle each value change event, stop is a little special because we want to close the hoster as well as the hosted VI. So in the hoster we create an override of "Catch Nested Value Update.vi". This VI is designed to handle messages sent from any sub actors or "nested" actors. The block diagram of the override looks like:
In the override we use an MVA function called "Unpack Nested Value Update" that outputs the label and value of the registered item (in this case the "stop" button). So in the "stop" case we stop the hoster actor which will in turn stop the hosted VI.
So now we'er almost done! We just need a "chef" to assemble our deconstructed hamburger on the serving plate. In every MVA application, the "Assembler" is responsible for inserting actors into actors. Let's take a look at the Assembler of our application.
I've added pictures and comments in the code to explain what's going on here. The red class is the hoster and the purple class is getting hosted. We use the MVA framework function called "Construct MVA Application" to stitch everything together. The "IViewModel" is the hoster (red class) and, in this case, the startup view is our UI being hosted (IViewable, purple class). When the program starts the MVA starts the actors and stitches them together.
What?! All that work for...??
Soooo what gives? This seems like a whole lot of complexity for not a whole lot of return. Here's the payoff:
In the next tutorial I'll show how the MVA framework let's us have N widgets in a UI working together. We'll start with "A Community of Two". So stay tuned!
What am I doing here?
I gave a presentation at the 2019 European CLA summit titled “Applying a SOLID Framework for Cleaner Code”. The slides can be found on the Composed Systems website. I had a great time at the Summit and it was great to meet and talk about LabVIEW with the hundreds of CLAs who attended. I made a large open-source demo project for the presentation that I’d like to expand on and give more design detail.
I have been working on some pretty large applications lately that have highly dynamic behavior. Actors are getting launched and closed, views are frequently popping into subpanels--that kind of thing. At some point pretty far into my development, I started getting this dialog when my executable (EXE) shut down. It seemed harmless enough, until I realized it was not going away...ever.
I did the usual web snooping and stumbled on this LAVA post. Infinite Reset of Death sounds about right (IRoD, as I now call it).
If you haven't already heard--or tried it for yourself--LabVIEW 2017 offers a simple but powerful new language feature that definitely warrants a deeper look.
Somehow, I made it all the way to the CLA Summit in Austin--having used LabVIEW 2017 for months--before learning about this. Obviously I neglected to scan the LabVIEW 2017 Features and Changes list. At the very least, this new feature addresses the common tedium of making typed polymorphic APIs. It also potentially addresses a design problem I've been banging my head on for months. (A little more on that later.)
This will be a short post on a minor annoyance with putting together a dynamic UI in LabVIEW. I am currently building a multi-subpanel UI with Stream and my simple actor implementation and was annoyed with the difficulty of registering for events on the individual panes in my UI. This annoyance is rooted in the fact that I neglected to label my panes as they were created. So I wrote a very simple little tool for identifying and labeling populated panes.
I've been wanting to write a network extension for Stream for about a year now, adding the ability to extend the mediator to the network and allow pub/sub between actors on different machines. I finally got the motivation to do it after starting to collaborate with Jarobit Peña Saez at Bits 2 Byte on the Stream project. This extension is a result of our collaboration efforts and the sharing of ideas.
One of the many things I can stand to improve upon is how I version my software. I say this because throughout my career I have never adhered to a strict versioning scheme for the software I write. This has been tolerable because my decisions typically only impacted me or at worst my team, but now that I am diving into the world of open-sourced software I think it is time to embrace a standard for software versioning.
I love reading about ancient Roman history and I also love reading about software design. Occasionally those two areas of interest smash together in my brain and weird ideas come out. This post is one of those weird ideas, I'd like to introduce you to Janus, the god of software.