Menus are now disabled when a procedure is running

A new feature of Panorama X 10.2 b21 is that the menus are all disabled while a procedure is running. In previous versions you could select a menu while a long running procedure was operating, which would completely disrupt the operation of the already running procedure – not good. So now the already running procedure is protected – you can’t mess it up by clicking, pressing a key, or selecting a menu. Much better, but this may turn up some previously hidden problems.

Today a user privately sent me a database where the menus were always dim. Once the database was opened, he couldn’t do anything. It turned out that the database contained a .CurrentRecord procedure, and that procedure contained a show statement. The show statement causes .CurrentRecord to run again, so .CurrentRecord was running continuously, over and over and over. (The .CurrentRecord documentation does warn about the possibility of infinite loops.) Before b21, he was never aware that this procedure was constantly running (though perhaps things ran a bit slow when this database was open, and I think eventually Panorama might crash from excessive memory use, though that might take hours or days).

I’m mentioning this because though this is probably a rare issue, it seems likely that it isn’t a one off – possibly someone else reading this will run into this. If you see that all the menus stay dim all the time, it means that there is a procedure running all the time. The .CurrentRecord procedure is a good place to start looking if you are using one, but there may be other undiscovered possibilities. If you do run into this, you’ll need to use a copy of b20 to open and work with the database, since that version will allow you to click and use menus even if a program is running.

That explains a problem I am having with one of my databases. I open it and cannot do anything with it, except quit Panorama.

I had the menu problem with a db (a really important one :wink: and so I opened it with b16, which was the only other copy of PanX I had. There was no “.CurrentRecord” procedure, and no other automatic procedures except “.Initialize”, in which I commented out a call to another procedure, then saved. When I tried to reopen it with b21, I got an error message, some problem with the .plist. (??) What now?

Beginning with b18, Panorama adds an integrity seal to a database when it saves. When it is subsequently opened, the database is checked to see if it matches the data in the seal.

When you use an earlier version than b18 to modify the database, the integrity seal isn’t updated, and that can look like corruption. Integrity checking can be turned off in the advanced portion of the preferences.

Thanks, Dave! That got the file open, and I think I’ve found the loop trigger. Seems like I should revert preferences now and re-seal the db for opens and saves, no?
Regarding the loop trigger: it appears to be interaction of the formFRONT: Form Event procedure with a ShowPage command. The Form Event procedure calls another procedure that performs some hidden operations, to display the data correctly, followed by an EndNoShow and ShowPage. When I comment the ShowPage out, the menus are no longer dimmed. That suggests that ShowPage triggers the formFRONT: Form Event procedure, causing the endless loop. My intent when I put that in there was to have the procedure triggered whenever that form was clicked on/brought to the front/made active… not necessarily, when the form display was refreshed. Is this a feature, bug, or just a good old “fact of life”? ;).

You can use the Check for Updates window to download previous versions of Panorama. Simply select the version you want from the Versions menu.

You don’t have to, but yes, that is probably the best course.

It does suggest that.

Form Event procedures allow you to plug in code into AppKit at a low level. This gives you great flexibility, but also exposes you to the vagaries and eccentricities of AppKit. Panorama triggers this code when AppKit says the window is activated. Apparently showpage makes AppKit think the window is activated. I don’t understand why, but AppKit often sends extra notifications that don’t seem necessary to me.

I think you nailed it with the last one.

FYI - I’m working on a diagnostic tool to help track down and fix this type of problem. I was hoping I would get it done today, but it’s proving to be a bit bigger project that I first thought. But I am making good progress. I’ve already got enough working that I can see that this is going to be a very helpful tool in general, not just for this particular issue.

What do you look for in a file with a disabled Initialize and no Form Events, yet opens with disabled menus? There’s no .CurrentRecord or any other procedure running, so…?

The file has no Auxiliary or Relations set but it does have a couple of simple lookups. If I open another file first that has the problematic file as an Auxiliary, the form fails to finish drawing and the Initialize of the first file stalls. Revert in the problematic file (that has usable menus in this scenario) causes the Initialize of the first file to complete and everything works properly in both files.

So what is it in a form that can be seen as a running procedure?

The diagnostic tool I mentioned on Sep 23 is now available, and I think it’s exactly what you need to diagnose this issue. See Including Procedure Triggers in Debug Instrumentation on this help page:

In addition, you might want to consider enabling debug instrumentation for the various Call statements, which can be done in the same debug instrumentaiton panel (Objective-C Panel). This will provide you with a wealth of information about what code is running in Panorama. The disadvantage is that your instrumentation log will get packed with a ton of data to sift through – but sometimes a ton of data is much preferable to no data at all.

( Nice Tool !! )

Well, it gave me just one line and that one line identified the issue but I’m surprised by it.

The problem proved to be that in a Tab Panel there’s a Procedure entered that is expected to be called when a tab is clicked. It doesn’t seem like that should be an implicit trigger. Once disabled though, the file opens as it should. I’ve used Tab Panels extensively and this is far from the first to have a Procedure attached. In fact it was in a past post here that you suggested it.

I’ll examine the called procedure for the actual source of the issue with consideration that it is being treated as implicit. Further tests have confirmed that the Tab Panel’s Procedure is implicit. But I do wonder what other form elements need similar consideration

Along the way, I clicked on the red button for Instrumentation Note, intending to give myself a marker. I couldn’t exit the window without an error and had to Force Quit. Subsequently it worked okay.

Screen Shot 2021-11-12 at 4.46.18 PM

Almost all Panorama form objects are based on one or more Apple AppKit (Cocoa) classes. AppKit classes have a mechanism for indicating that a change has occurred and that the program should take action. When Panorama sees this notification, it runs the code associated with the form object. So Panorama simply passes thru these notifications. AppKit classes tend to be a bit “chatty” and I’ve also observed that they will indicate an action is needed in circumstances where I might not expect that to be the case. But Panorama has no way to second guess this, so if the AppKit class says action should be taken, your code will be called.

Put another way, since these actions are not originating in Panorama’s code, but rather in Apple’s code, I cannot tell you what other form elements need similar consideration. Fortunately now the Debug Instrumentation makes it easy to discover the origin of triggered code.

Through the Instrumentation Log I’ve found the cause of a lot of my startup issues and many hours of frustration. It turns out that at least some graphic objects in a form run their embedded procedures before the .Initialize procedure runs. It definitely happens with Tab Panels. Obviously that can lead to errors with variables that have not been defined or any other conditions that rely on the .Initialize to set up.

It was probably the cause of a problem I reported some time ago about Tab Panels in different files corrupting one another when files and auxiliaries were launched.

Is it possible for you to get .Initialize to run ahead of objects’ procedures, or should I consider a simple startup form for every file?

The .PreInitialize procedure might be useful here. The only documentation I could find for it was in the release notes for the version where it was introduced.

  • Added new .PreInitialize procedure that runs before windows are opened. This should not do anything except initialize variables. It definitely should not perform any UI changes or display, so it should NOT contain showvariables, and it should not modify the values of any fields. Just use this for setting up fileglobals. Also, don’t make any assumptions about what window is currently open – it should not reference windows at all.

Well, .PreInitialize is one that I had missed. I’ll have to see how much of a remedy it is.

I’ve been checking and testing a variety of objects and so far it seems that only Tab Panels run their procedures as a result of being drawn. All others remain idle until clicked or otherwise triggered by an intended event.

At present, Tab Panels with procedures are a problem if they’re in the form that opens when a file is launched.

I moved Tab Panel procedures to Form Properties for each of the forms the Tab Panel uses and that too seems like a resolution.

FWIW, the log shows that every Tab Panel still tries to run a procedure, even when none is entered.

TabPanelObject RUN CODE --> 
TabPanelObject RUN CODE --> 
TabPanelObject RUN CODE --> 
RUN CODE --> .Initialize (PartsList)
TabPanelObject RUN CODE --> 
TabPanelObject RUN CODE -->

Another option is to include in each of those called procedures a delay loop that doesn’t allow the procedure to run until the .Initialize procedure has assigned a value to a variable, which it would do as its last task.

No, that’s not possible.

This isn’t documented because I’m not satisfied that this feature works correctly. So I wouldn’t use it.


What I would do is write your code for the tab panel defensively so that it doesn’t matter if the .Initialize procedure has run or not. I assume that the main problem is that variables are referenced that haven’t been set up yet. The catcherror( function is super useful in this case. Suppose the code references a variable named myAreaCode that is normally set up in the .Initialize procedure. In your tab panel code, you can use this formula to reference this value. In this case I’m putting in my local area code, but usually "" or 0 is the best option.

catcherror("714",myAreaCode)

You could use a similar technique to skip the rest of the tab panel code if .Initialize hasn’t run yet.

if catcherror("999",myAreaCode)="999"
    return // skip rest of tab panel code, we're not initialized yet
endif
... rest of tab panel code

I’m assuming that 999 will never be a valid value for this variable. If that’s not the case, use some other value.

9 posts were split to a new topic: Uninitialized variables and the aggregate( function