QControl Enthusiasts

cancel
Showing results for 
Search instead for 
Did you mean: 

The DSM Datagrid (yet another Datagrid QControl)

I'm happy to say I just got permission to release this Datagrid QControl to the community. It's my first publicly released project, so be gentle :). My company (Dynamic Structures and Materials, hence the DSM) mainly uses LabVIEW for internal dev and specific contract work, not toolkit development, but I wanted to release this. NI is, of course, releasing an official Datagrid package sometime in the next year or so (?), but who knows- maybe someone can find this useful.

 

This differs from the example QControl in a few ways. (I'll admit I'm not 100% familiar with that one so forgive me if I'm mistaken here, I had a few issues with getting it working).

 

An overview of various differences between other datagrids/features/etc:

 

  • This doesn't use a Wizard to configure it. It's done entirely on the block diagram. This makes it a bit cumbersome to set up, but it means you can add, remove, or reconfigure elements at runtime very easily using the same techniques you used to construct it in the first place. Want to change your Ring control to a Boolean? Just swap the elements at that location.
  • This uses a Table control instead of a Multicolumn Listbox.
  • The underlying data storage is a 2D array of DatagridElement.lvclass. All elements inherit from this base class. If you want to add a new element type (like a color selector or calendar control), you can make your own element that overrides this base class and implements "EditValue", "Handle mouse enter", etc. See the example types for more info.
  • Data in the 2D array is stored exactly as LabVIEW stores it. This eliminates potential (very small) rounding errors in String to Double conversions. For example, if you need a Float datatype, you can have a Float datatype- not a Float stored in a Double. This is important for "Get flattened data.vi", which returns a U8 array that contains your data. This is actually the reason I made this library; I needed exact byte data for all of the elements of a bunch of configuration parameters that would be sent through a checksum algorithm. If I simply used a U32 with a max coercion value of 256 to represent a U8, the byte pattern would be wrong and my checksum wouldn't work.
  • The whole shebang is, of course, object oriented, so if you want to make your own special datagrid you can override this one. That would let you implement very specific events that could signal the owner VI instead of receiving generic element info that you have to typecast to its correct value. This is an unfortunate downside of runtime configurability; signaling events have to return variants, string versions of data, or flattened U8 arrays. There is only one event handler that gives you "item touched" events. Want to store a cluster as the base datatype of an element? You can do that!
  • There is a "programmatic edit" functionality that programmatically triggers a mousedown event at a certain cell. This is a user event output from "New D_Datagrid"; send a coordinate to that user event, and the element will act like you clicked on it. Useful for auto-triggering a dropdown or for your own custom usage.

The following datatypes are supported out of the box:

  • String dropdown box (i.e., a ring control)
  • COM port selector (implemented as an override of the String dropdown)
  • Boolean (acts like a button)
  • String
  • Float
  • Double
  • U8
  • U32

Numerics are set up as another subclass so you could implement an I32 or something easily enough.

 

Known issues:

  • There is a bug that makes the first click or two show up slightly in the wrong spot. I can't reliably reproduce it but it's only the first click or two.
  • Documentation is... about on-par for user submitted stuff. Using the datagrid out of the box is reasonably documented (IMHO) but overriding and creating your own classes is a bit more involved, and the documentation is basically "just refer to the existing datatypes". Apologies for that.

Here's the example VI with a couple comments- it's bulky to start but you can easily hide this in a subVI, or wrap it up in your own derived class:

BertMcMahan_0-1651075282668.png

 

Message 1 of 20
(3,476 Views)

I was doing a bit of thinking and realized I may not have explained some of the benefits to this particular Datagrid approach, so I thought I'd bring them up.

 

Since the entire Datagrid is OOP and each element is a class, it's very expandable and can do MUCH more stuff than just hold a single value. The underlying mechanism is that when you click a cell, a VI called EditValue runs via DD on that element. You then interact with that running VI, which in most cases is just a small front panel with a control of the desired type on it.

 

That lets you benefit from all of LV's standard types, but it also means you can handle a LOT of UI stuff within the Datagrid itself that you'd otherwise need to handle externally.

 

For example, the "COM port selector" element type mimics the behavior of the built-in VISA selector. It overrides and extends the Ring control element, which gives the user a list of items in a dropdown. The COM port selector actually calls VISA functions to populate the internal list on every MouseDown event, before the dropdown opens, so each time you click it you get a current list of VISA resources. This is all contained within the Element's class, and your caller VI doesn't need to know anything about VISA to do it.

 

That's just a simple example. A more complicated example might be an element that maintains a history of values written to it, and which shows a popup history chart when you click the element. You'd just need to add a chart to the front panel of the EditValue VI and play around with sizing.

 

You could even have a "callback" type, where you add (at runtime!) callback VI's that run when the value is clicked/edited. The Boolean type right now just toggles values when clicked, but you could register some action to happen when clicked as well, and you could change that dynamically. All of this would happen within the Datagrid's event handler, not the main program's. It's debatable if that leads to poor usability, but hey it's possible to do it!

 

The last fun element type I thought of would be... another Datagrid! It's not trivial, but it wouldn't be hard to make the subtype be a Table control that pops open a new VI with more elements in it. That could be cool for some lesser used settings. For example, a "PID settings" box could have "Click to edit" or "<-->" as its "String" data, and when you click it you get a P, I, and D input grid on the flyout. Since those are Datagrid elements themselves, you could add range checking and verification to the inputs as well. That kind of thing always tends to clutter up my main UI event handler, so keeping it tucked away somewhere else would be a nice way to declutter the screen.

 

And since it's all defined at runtime, you could populate this entire list, including valid ranges, element types, sub-menu element types, etc. from a database read at the start of your program.

 

I don't have time at the moment to make a prototype of that but it wouldn't be TOO terrible to do 🙂

Message 2 of 20
(3,398 Views)

Lots of good stuff here. We're looking to convert an XControl that does something similar with using other controls, panels in your case, to overlay the cells of a table for editing. The only drawback I see is that in your control, every cell of the table becomes a class object so even if a cell is just a basic string, it would have to be converted to an object. Is this conclusion correct?

"All truths are easy to understand once they are discovered; the point is to discover them." -- Galileo Galilei

0 Kudos
Message 3 of 20
(2,741 Views)

Each cell has its own object, yes, but you don't have to do a lot of conversions back and forth yourself. You'll need to drop a "String element" object on your diagram to define the cell, but once that's done you can just read and write the value using the setter and getter methods.

 

I personally don't using objects as a drawback as I'm quite comfortable using them, but I know that's not true for everyone using LabVIEW (unlike, say, Python, where AFAIK literally everything is an object already).

 

If you don't need to do much with custom types you don't need to worry too much about the Object side of things. You'll need to do a little bit, but not a ton. It's really only necessary if you're trying to make your own new type (like a colorbox, or a calendar, etc).

 

The object nature of these lets you have all sorts of extra underlying info or data rather than JUST having the info displayed in the box on the screen.

 

For some examples, a Colorbox control might let you select a "color", but behind the scenes you may need to represent it as RGB, or CMYK, or some other representation. With classes, you can have all of that info stored in the class object. Similarly, with a date selector, a "date" might show up in the text field, but behind the scenes you can store it as UTC with a time zone so there's no confusion later on. A COM port selector can show a string, but it could be aliased internally to the class.

 

This does add a bit of overhead when it comes to "I just want to show a string" but it's overall not too difficult (IMHO) once you get the hang of it. If you're not a fan of using OOP, just pretend each element is a cluster typedef with a single element and you'll be 90% of the way there.

 

If you have any questions please feel free to ask them. I'm well aware the documentation is quite sparse, and for that I apologize.

0 Kudos
Message 4 of 20
(2,734 Views)

In my version I made it OOP for the same reason Bert did, to make the conversions to and from what is displayed in the multicolumn listbox dynamic dispatch. It also makes it easier to create new data types because the interface is defined by the parent class.

 

Some differences between mine and Bert’s, Bert can correct me if I am wrong, is that mine has an edit time dialog, invoked by a right-click menu, to configure the data types by column. All of the rows in a column have the same data type. So there is really just an object definition per column. Bert’s, I think, is flexible to define each cell and can be defined programmatically at runtime. I have not added the functionality to make mine programmatically definable at run time but it could be added. The problem that could arise, however, is what happens if the data type is changed and the data can’t be parsed correctly. That is why it is currently only changeable during edit time.  

Quentin "Q" Alldredge

Chief LabVIEW Architect, Testeract | Owner, Q Software Innovations, LLC (QSI)
Director, GCentral | Admin, LabVIEW Wiki | Creator, The QControl Toolkit
Certified LabVIEW Architect | LabVIEW Champion | NI Alliance Partner



Message 5 of 20
(2,730 Views)

TheQ, that's correct. Mine in fact can only be set at runtime, so that's a potential downside if you want to get it looking a certain way without needing to run it over and over again.

 

My use case needed to load the grid dynamically from a config file that the user created. The original use was to display a memory map for some low-level hardware, so they'd define the interpretation of each memory location using a text file and the grid would interpret the bits that way. This is also why I used a lot of different, but similar, classes. For example, instead of a single "integer" type I had U32's, U8's, I32's, etc- I had to interpret the memory map exactly, so when a user typed in a new value I needed them to interact with an actual data element on a floating front panel instead of a string control. Since I kept the underlying data in the background, I didn't have to worry about converting to and from strings each time I changed things. The user typed elements into actual Float controls, which let me use LabVIEW's built-in limit checking, etc. I converted it to a string for the display, but when I calculated the "bits" to send to the hardware I used the actual, stored-as-real-data Floats in the background.

 

I haven't needed to actually change the type dynamically, so changing the type at runtime (and trying to preserve the value) hasn't come up yet. I'm not sure I can think of a personal use case where I'd need to do that so I haven't worried about it yet.

 

It's also been important for me in several uses, not just my original one, that you can have different types spread throughout instead of defining them by column. It's certainly more work to do it this way, but it lets me do things like [label\value] pairs in a 2-column, n-row list. I can have booleans, floats, strings, COM ports, whatever all in the same column.

 

I do wish I'd thought a little more about the interfaces to the table I used. It would be very useful to add this to a Tree control, for example.

Message 6 of 20
(2,720 Views)

Thank you for the replies gentleman. In my case, I need the cells within one or two columns to be of different data types per row for editing purposes, which is where Bert's solution comes in. I will be using a tree control to drive what is loaded in the table, while one column is updated at a more constant rate from a data source (live values). And the data types per cell will be dynamic for those two columns (as the tree selection changes, new signals are loaded). So, lots of things to figure out.

 

I'm not opposed to using objects, it's just the conversion of string data to an object for strings with no additional features seemed unnecessary.

"All truths are easy to understand once they are discovered; the point is to discover them." -- Galileo Galilei

0 Kudos
Message 7 of 20
(2,679 Views)

That's understandable but IMHO having strings be a special case is more confusing than it is helpful. I'd say that "Everything is an object, even things that don't need to be one" is more consistent than "Everything is an object except for strings".

 

The datagrid in memory is a 2D array of objects. What you're seeing on the screen is just the "To String" version of that basic memory item. If you wanted to, you could skip the string box popup and let the user type directly into the grid for string inputs, but that breaks the more general pattern of having a popup window to handle the user input. Then again, the Boolean type doesn't have a popup either (it just toggles when you click on it) so having Strings be a little different wouldn't be the end of the world.

0 Kudos
Message 8 of 20
(2,673 Views)

Hi!

Really neat library here.

Is there source code available in a GIT repo somewhere?  Or just the VIP here?

0 Kudos
Message 9 of 20
(1,930 Views)

It's just in the VIP, sorry. We use SVN internally and I haven't played much with Git so I haven't stuck it online yet. Thanks for the idea though, maybe I'll find some time to stick it up there.

 

(That said- all of the source code is in the attached VIP, so you have everything you need to play with it if you'd like.)

0 Kudos
Message 10 of 20
(1,895 Views)