What is the best way to store tabcontrol data in userdata fields?
| Project: | TabControl |
| Component: | Miscellaneous |
| Category: | feature request |
| Priority: | normal |
| Assigned: | Unassigned |
| Status: | active |
Hi Adam:
I see both you and I have designed Tab Control packages that do similar things, namely, help automate tab control action, and minimize the tediousness of writing functions that hide and show controls when a tab control is clicked. Both of us found the user data fields to be a good place to store information on which controls belong to which tab, and both of us have made control panels which provide a GUI for adding and removing controls to/from tab controls. I like your code to deal with subwindows, and the option of calling not one, but two additional tabcontrol procedures, one before and one after showing/hiding the controls.
One thing I like about my approach is that I keep the database of controls in a user data field for each tabcontrol. Each tab of the tabcontrol has a userdata field with a list of the controls that belong to it. There is also an additional userdata field for the tabcontrol that contains the showing tab. Because the tab control procedure gets the name of the tab that was clicked, and reads the name of the current tab from the userdata, it can hide the controls belonging to the current tab, and then show the controls belonging to the new tab, without having to iterate through the entire list of controls on the control panel. This speeds things up a little for complicated panels. In a previous version, I also stored the number for the control type in the userdata field as well, which prevented a call to controlinfo for every control in the list. I stopped doing that to make a cleaner looking, more user editable control list. I guess I need to do a speed test to see how much of a difference in speed that call makes.
I hope Igor Exchange will help prevent this sort of duplication of effort, and maybe help collaboration between people doing similar things. Perhaps we could combine the best features of our two procedures.
Also, as everybody needs a good tab procedure, I think I will suggest on the "wish list" forum that it be a part of Igor Pro; I'm sure it would run faster as a compiled C code.
cheers,
jamie
Updates
Jaime
I saw your package when you posted it and thought it was pretty funny that the two of us came up with very similar solutions to the problem. I think you wrote your package first, and I don't recall having seen it before I wrote mine, but they sure are similar.
When I was designing my package I wanted to make it very easy to move controls to a different tab without having to first know what tab the control was placed on. I see your point about having to iterate through all controls, but honestly I haven't come across a case where the time it takes to do this is a problem. My most unwieldy panels have maybe 50-100 controls on them though, so I suppose if you had hundreds of controls there might be some perceptible drag during the changing of the tab.
I do hope that IgorExchange will help to prevent this kind of duplication of effort in the future. As for combining the two projects, that would be great if we could make it work. Several of my applications depend on the two tab change hooks as well as the ability to host subwindows on the controls.
Adam
Have you tested this for compatibility with Igor 6.02's "native GUI" appearance?
One thing to look out for is the drawing order of the controls. Define the tab first and the controls within the tab seconds.
Jim Prouty asks:
appearance?
One thing to look out for is the drawing order of the controls. Define
the tab first and the controls within the tab seconds.
No. There is a nice GUI for adding controls to existing tab controls on a selected panel, but it doesn't explicitly check for drawing order. It doesn't update the window macro to reflect the new userdata either.
Could there could be a button on the panel to update the window macro for the selected panel? We could use the WinRecreation function to get the window recreation string, add the user data, and at the same time, parse the string to make sure the tabcontrols are drawn before the other controls. This could be an interesting problem to solve for nested tab controls - making sure the root-level tab control is drawn first. The other problem is saving the edited panel macro. According to the docs, the /R code for the doWindow command does nothing if a macro or function is running. How then does one programmatically alter a window recreation macro? Is there a better solution than saving it as an entry in a notebook and expecting user to cut and paste?
@Jim
I did test both the example experiment in the package as well as the tab controls I use in my equipment control packages, and the tabs work fine with the new beta version of Igor (with the exception I sent to support@wavemetrics)
As Jaime said, the package doesn't change the order of controls either on the panel itself or in the recreation macros.
As for Jamie's idea of automatically writing the recreation macro and/or modifying the order of controls in the recreation macro, I personally think that's best left to the developer to do. Having the TabControl package check for the order that controls are drawn would move it from something relatively simple into the direction of an "automatic GUI builder" type package. That in and of itself might be an interesting project to work on, but I think it's beyond the scope of the TabControl package itself.
I realized we can update a panel macro with the Execute/P command. So that's one less problem. It would be nice to have a way to send controls to the bottom of the drawing order without having to kill and redraw a host of controls. May I request a way to do that from within a user function? Or, as I suggested on the Igor WishList thread, just move the showing and hiding of tabs wholesale into IgorSpace.
