Popular Post hooovahh Posted August 12, 2013 Popular Post Report Share Posted August 12, 2013 NI Week was a blast and while there I got to mention a few things I was trying that I'd like to share with the community. One of them is this thing I'm calling a Multi Panel Interface (MPI). The code is rough around the edges, very rough. This is probably the least polished code I've posted in a long time but this code has been sitting in a half working state for a while, and if I took the time to clean it up, it might never be ready. So please be gentle, there's room for improvement but I think the basics are there. Now with that out of the way here's what I got. I wanted to make an interface similar to Veristand. I don't want to re-create the wheel when it isn't necessary but anyone who has worked with Veristand knows that there are quite a few limitations, and as a result I started developing a UI that is dynamic. So that controls and indicators can be added and setup in way that could be saved and loaded for different operator types. This would be setting and getting event based information from Actors running asynchronously. Implementation It is a publisher subscriber architecture. Here there are actors for each set of hardware we care to read from. Later we could talk about controlling Actors but for now lets just talk reading. So for example there maybe an Actor for CAN, Power Supply, AIO, DIO, FPGA, Real-Time (talking to another PC), UPS, etc. Each Actor has a functional global that describes what data it has for other panels to display. For the time lets call any data an Actor is publishing a "channel". An Actor will publish the channel data to user events, and in a global it will have the list of user events and their names so they can be subscribed to. The actual messaging system between the Actors and the Panels can be anything and in my demo I actually just have random numbers because the demo isn't meant to showcase the messaging it is meant to showcase the UI. UI The purpose of the demo posted is to show that a tabbed interface can be made, and be made semi-dynamically. There are 3 things to be defined to understand what is going on. The Parent VI, the Panel VI, and the Child VI. In the demo the Parent is the top level "Main.vi". Clicking the "Add" button will add a Panel VI which is a shell of a VI that has controls to minimize, pop out, and control menus to the Child. The Panel has a subpanel that contains the Child VI. The Child VI in this demo is only a graph showing random data. The Child VI is tied to the Panel VI using a subpanel. The Panel VI is tied to the Parent VI using Windows user32.dll function calls. The user can interact with the Child anyway the child defines. There is no right click override (like in Veristand) where you can no longer right click on a graph and get a menu. The Parent VI also has little restrictions. It is a normal VI with a menu bar which can be used for things like managing Panels. In the demo there is also tabs in the Parent. They are fake tabs in that there are 20 but the ones not being used are hidden. There are neat UI elements that are hard to describe in text. Things like docking/undocking Panels, minimizing Panels while docked or not, resizing Panels while docked or not, moving panels quickly and easily, renaming panels, intuitively adding tabs while moving panels. These are the basics that I didn't know if I could do so I did first. The messaging I described before is not difficult it just takes some time to define how Actors talk and how Panels subscribe to the data. I know there are some UI bugs. The biggest one that you'll notice is trying to move a Panel when docked will have some z-order issues with other Panels that are docked, but there are others. Code The actual code in the demo uses several pieces of code I've found. The parent child idea I originally saw in the Dynapanels demo. Then there are several user32 functions from the Windows API. Panel controls were from UI Panels. OpenG Array, Error, and Application Control is needed, I'm not sure if there are others. http://screencast.com/t/P9SJ1anR Please feel free to comment and use as you see fit. Just know I won't be supporting this as a real project. Multi Panel Interface.zip 4 Quote Link to comment
todd Posted August 13, 2013 Report Share Posted August 13, 2013 Cool, thanks for posting this! It reminds me a little of the panel handling in ALOHA. I'm in the (background) process of writing an AF child that encapsulates all the panel handling (and announces data streams). Quote Link to comment
hooovahh Posted August 14, 2013 Author Report Share Posted August 14, 2013 I've never heard of ALOHA but might check it out. If it is an unbounded number of panels I assume they are doing similar techniques with Parent Child relationships. Using subpanels alone does work but you have a limited number because you cannot add a subpanels (or any other control) at runtime. Quote Link to comment
Norm Kirchner Posted August 14, 2013 Report Share Posted August 14, 2013 Is using the win32 'child window' declaration a hard and fast requirement? Quote Link to comment
hooovahh Posted August 14, 2013 Author Report Share Posted August 14, 2013 (edited) Is using the win32 'child window' declaration a hard and fast requirement? Well...do you have another mechanism for attaching floating windows to other windows? If you go with subpanels you can do it but you need something like 100 subpanel controls, all hidden on start, and then when you need to insert a VI you go get one and move it to the location you want and insert a VI. But if you need 101 controls then you need to go and make more subpanels in the source and make a new build. If subpanels could be added programatically at runtime this would be fine but you can't so I don't know another way to do this. By the way, I have tested this UI design with subpanels and it does work, but it is more limited. EDIT: Also things like panel resize while docked gets a little tricky with subpanels, where the user32.dll gives access to allow a window to be resized like a floating window. Edited August 14, 2013 by hooovahh Quote Link to comment
GregSands Posted August 14, 2013 Report Share Posted August 14, 2013 If subpanels could be added programatically at runtime this would be fine but you can't so I don't know another way to do this. Your code already has this problem with tabs - what if someone wants more than 20 pages? But since Tab Controls are Enums, I don't think there'll ever be the facility to add pages at run-time. Quote Link to comment
ShaunR Posted August 15, 2013 Report Share Posted August 15, 2013 Your code already has this problem with tabs - what if someone wants more than 20 pages? But since Tab Controls are Enums, I don't think there'll ever be the facility to add pages at run-time. If you use the OS tab control via activex or .NET. you can add/remove pages dynamically. Quote Link to comment
hooovahh Posted August 15, 2013 Author Report Share Posted August 15, 2013 (edited) Your code already has this problem with tabs - what if someone wants more than 20 pages? But since Tab Controls are Enums, I don't think there'll ever be the facility to add pages at run-time. I totally knew that going into it. But I feel like having 500 tabs and have them be hidden is not a big issue, but having 20 subpanels for each tab making 10,000 subpanel controls just not being used, would be overhead that may have an effect on performance. What I'm saying is the 20 page limit is arbitrary sure, but can be a very large number without any real issue. If you use the OS tab control via activex or .NET. you can add/remove pages dynamically. I never thought about this. It sounds like it might be a good idea to try since adding/removing pages dynamically would be a nice feature that the native tab can't support. EDIT: or VIBox might work too. Edited August 15, 2013 by hooovahh Quote Link to comment
ShaunR Posted August 16, 2013 Report Share Posted August 16, 2013 (edited) Had a chance to look more closely now. You can save yourself having to load "special" VIs that take a custom event via controls or detecting mouse over/move events on the child FP (checking mouse coordinates in region etc in the main VI) by attaching a callback to the VI when you load it. The callback can be attached to any VI to piggy-back the child VIs events and inject your own user event that you can access in your main VI. You can attach these call backs to not only the VI server events (close, mousemove, resize et. al.), but controls/indicators as well. I think it would greatly simplify you main VI. Edited August 16, 2013 by ShaunR Quote Link to comment
hooovahh Posted August 16, 2013 Author Report Share Posted August 16, 2013 Had a chance to look more closely now. You can save yourself having to load "special" VIs that take a custom event via controls or detecting mouse over/move events on the child FP (checking mouse coordinates in region etc in the main VI) by attaching a callback to the VI when you load it. The callback can be attached to any VI to piggy-back the child VIs events and inject your own user event that you can access in your main VI. You can attach these call backs to not only the VI server events (close, mousemove, resize et. al.), but controls/indicators as well. I think it would greatly simplify you main VI. I don't fully understand what you are saying but am curious. I have used event call backs on .net DLL calls so I am passingly familiar with them but I don't know how they could help simplify my code. So when the mouse enters my panel VI I can have that run a VI through the callback feature, which can generate a user event, that tells the parent this event has happened? I don't see how that is better then at the moment where the VI ref can be passed to the parent, then register for the mouse enter event, causing the event to be handled in the parent when the mouse enters the panel. Don't take this post as me saying "This is stupid and my way is better" I just don't fully understand how it can help because I've had very little experience with event callbacks. Quote Link to comment
ShaunR Posted August 16, 2013 Report Share Posted August 16, 2013 I don't fully understand what you are saying but am curious. I have used event call backs on .net DLL calls so I am passingly familiar with them but I don't know how they could help simplify my code. So when the mouse enters my panel VI I can have that run a VI through the callback feature, which can generate a user event, that tells the parent this event has happened? I don't see how that is better then at the moment where the VI ref can be passed to the parent, then register for the mouse enter event, causing the event to be handled in the parent when the mouse enters the panel. Don't take this post as me saying "This is stupid and my way is better" I just don't fully understand how it can help because I've had very little experience with event callbacks. It does run the VI, but the VI is attached, externally, (for want of a better explanation), so you do not need any code whatsoever in the child to get it's events and the top level VI doesn't co-opt the childs events (the child can behave as as a discrete module-handle its own events and broadcast them when actioned to the parent). I'm also not saying it is better. It's just a technique I have found that keeps responsibilities modularised and the parent clean of module specific event handling. Quote Link to comment
hooovahh Posted August 16, 2013 Author Report Share Posted August 16, 2013 It's just a technique I have found that keeps responsibilities modularised and the parent clean of module specific event handling. I am all for this type of development, and I think I can see how this could help with that. Thanks. Quote Link to comment
Jay0909 Posted September 26, 2014 Report Share Posted September 26, 2014 Hi, I had a question related to Load and save settings in your program. I just wanted to know how would you implement save and load front panel vi's? also, if you load Front panel settings, how would you place all the vi's exactly on the same location when saved? Quote Link to comment
hooovahh Posted September 26, 2014 Author Report Share Posted September 26, 2014 I had a function a while ago and I'm not sure if it was included in this post but it would give relative position from a parent to a child, or absolute position from the desktop to the child. Saving this information, then using the Windows Move function on load could be done relatively easily. Now if you have custom connections for a child window you'll need some custom way of re-making those connections on load. This demo just shows a random number generator for a window, but one could see that a publisher subscriber architecture could be used where a child window subscribes to data to display. And these are the connections I am talking about that would need to be saved and loaded as well. Quote Link to comment
Jay0909 Posted October 1, 2014 Report Share Posted October 1, 2014 Hi, I had question regarding the custom connections. For eg: User event for custom loading is created, similar to quit event. Next step, try to load Custom UI, but it applies to all Child vi's on User interface like Quit event, in this case how to make connection to load only particular child vi. Quote Link to comment
hooovahh Posted October 1, 2014 Author Report Share Posted October 1, 2014 Okay so this is all conceptual and I haven't actually tested any of this. Notice that in the Run Panel case in the Main.vi you are creating a variant to give to the child. This variant is actually similar to a Variant Repository I posted earlier but that is a different story. In this Variant we embed the same Quit user event. We also embed a Set Settings event, a Get Settings event, and a Get Settings Reply event which is unique for each child. The idea is that you could keep this information and can send user events to one child at a time, or throw it in a loop and set all of them. Quote Link to comment
Jay0909 Posted October 6, 2014 Report Share Posted October 6, 2014 (edited) Hi, Thanks a lot for the reply. I want to be more specific, In the main vi there is a case called quit Vi , In that i actually replaced quit and made it set settings and removed for loop.In the child VI (Numeric graph), there is a case set settings i created a erase history. I used a runtime menu for set settings. I loaded 2 or 3 child vi's on FP and tried what u mentioned above, but all the vi's Graph history was erased. So could please give me a solution of how to make unique for each child? if any example pls do post. Edited October 6, 2014 by Jay0909 Quote Link to comment
hooovahh Posted October 7, 2014 Author Report Share Posted October 7, 2014 No I have no examples. This was just test proof of concept code and I have yet to use it in an application. I don't fully understand what you are trying to do. In the example there is the global event (or rather an event for all children) which is Quit, and then there are events for each child like Set Settings as mentioned earlier. You can generate this Set Settings event which has a variant as the data, and depending on the value of the data do different things. Like the data of the variant can be a cluster of a String, and a variant. The String can be the type of setting you are attempting to set, and the variant can be the payload of the event. I personally use Variant attributes but that is harder to explain if you are unfamiliar with them. Post some code if you are still confused. Quote Link to comment
Jay0909 Posted October 8, 2014 Report Share Posted October 8, 2014 Hi, I had a question, for eg: if i have 50 child vi's on FP, and if i generate a user event from main Vi, which child vi will respond first? When i generate user event in Main vi, how does actual reach to all child vi, I mean to say one by one or simultaneous? And also In the above example if 50 child gets event at the same time. How do v actually recognise all 50 response from child vi's? So, what's the method to get response one by one from all child vi's ? Quote Link to comment
hooovahh Posted October 8, 2014 Author Report Share Posted October 8, 2014 Well the idea is to use a publisher subscriber design. So this means you can communicate 1 to 1, or 1 to N from the parent, but only 1 to 1 from the child. So you could send out a single global event that all children get. This would then be acted on at more or less the same time on all children at once. Just like how you send the Quit and they all quit at once, no child is waiting for another child to quit. As for the response. Because the design is 1 to 1 from the child perspective, I'd say you could send out a global out, from the parent, then each child can generate the same event back to the parent, with different data identifying which child it came from. Looking at my code I feel bad for not completing this part which was in my head but not in my code. Notice that again there are 3 events made which is unique for each child. Set Settings, Get Settings, and then Get Settings.Reply. The intent was the parent could send 1 to 1 using Set Settings. And the parent could get data by sending a 1 to 1 on Get Settings, which would cause the child to send back data on Get Settings.Reply which the parent would register for. I also can't believe how many subVIs don't have icons, such a shame. Quote Link to comment
Jay0909 Posted October 20, 2014 Report Share Posted October 20, 2014 Hi, In this topic you have discussed about, reading analog, digital Inputs etc. from a hardware using actor framework. I'm really interested in learning this concept, Could you please tell me in Detail how could you achieve in reading analog or digital signals and passing on to child vi's using user events. Quote Link to comment
hooovahh Posted October 20, 2014 Author Report Share Posted October 20, 2014 Again I'd like to stress that this has never been done with this design pattern but could be done, and could be done a multitude of ways. Let yoru DAQ actors start up, and then have it publish to a functional global (or something) an array of signal names that the actor is currently reading, and an array of user events for each signal. Then have a way for your child window to select a signal from an actor. Maybe a right click option, or use the same menu for renaming the window. Once a signal is selected that window could then register for the user event that corresponds to that signal selected. Then back at the DAQ actor, generate a user event for each of the signals being measured when there is new data. This way the actor is publishing, and the children are subscribing. The publisher doesn't know how many subscribers there are and it doesn't really matter. Unless this is done on RT I'd suggest not generating an event for each new sample of a signal, but instead generate an event and send over N samples either in an array or a waveform. Then when the child gets new data, you can append this N samples to the end of the graph currently being displayed. This isn't perfect and needs some through about the different supported data types, but it could scale well if done right. This technique could be applies to non synchronous communication too. Lets say you are looking for a CAN bus signal that is sent by another device. You could subscribe to the data in the child window in a similar way, and then when no new data comes in, you know that signal hasn't been seen. Quote Link to comment
karolek Posted May 14, 2015 Report Share Posted May 14, 2015 (edited) When it comes to drag and drop, I found that your solution doesn't work for fast mouse dragging. Because when dragging, other UI events will be disabled (no need for them), I found it more useful to create some while loop inside Mouse Down event that will upload front panel bounds and poll Mouse Button to check if it's up: Drag and drop.zip Edited May 14, 2015 by karolek Quote Link to comment
hooovahh Posted May 14, 2015 Author Report Share Posted May 14, 2015 Your upload is missing a file, luckily it was just a type def'd cluster and I could disconnect it to see how it worked. From your description I didn't think I'd like a design where a while loop is inside a case of an event structure, but having it be encapsulated in a reusable subVI makes me like it for some reason. I also don't like how this is polling in a while loop where an event structure could generate new events for a mouse move, but then again the refresh rate is configurable, and it is only for the time the mouse is down. A different design could be using the Limit Maximum instances of a mouse move event to 1. But then that's more work in the VI using this function, where your technique is pretty easy to incorporate into a new VI. It could be reduced even more simply by the init and close in the subVI, possibly keep the reference open with an uninitialized shift register. You can also read the calling VI reference. The result could be a single VI, that you put into a Mouse Down event, that takes care of a window move. Attached is some of these improvements. Thanks for sharing. Drag Drop Hooovahh Edit.zip 1 Quote Link to comment
Kungfu Posted February 3, 2017 Report Share Posted February 3, 2017 Hey Guys, I just accidently found this topic and its totally feasable for my Automatic Testing Project. Unfortunately i cannot download the Multi Panel Interface.zip. Could anybody please repost it? Best regards, Kungfu Quote Link to comment
Recommended Posts
Join the conversation
You can post now and register later. If you have an account, sign in now to post with your account.