04-27-2022 11:01 AM
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:
The following datatypes are supported out of the box:
Numerics are set up as another subclass so you could implement an I32 or something easily enough.
Known issues:
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:
05-20-2022 10:59 AM
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 🙂
08-02-2023 07:02 AM
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?
08-02-2023 10:37 AM
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.
08-02-2023 12:13 PM - edited 08-02-2023 12:23 PM
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.
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
08-02-2023 12:57 PM
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.
08-07-2023 06:39 AM
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.
08-07-2023 10:22 AM
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.
02-09-2024 01:04 PM
Hi!
Really neat library here.
Is there source code available in a GIT repo somewhere? Or just the VIP here?
02-13-2024 12:04 PM
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.)