7
$\begingroup$

I am looking to create a Dynamic interface in a notebook that will load itself from one or more packages at run time when it is opened. The interface also uses DockedCells, which also must load content at run time.

I have the following issues:

  • I would like to avoid rebuilding the notebook when the packages are updated
  • Need to prevent function evaluation when creating the notebook
  • Dynamic calls in DockedCells slow down the interface dramatically such that it is unresponsive to user actions like moving the window
  • If multiple copies of the notebook are opened they can not share variables

What is a robust method to accomplish this?

$\endgroup$
5
$\begingroup$

This is the method I have worked out after a lot of trial and error, but I would appreciate any suggestions for improvements to it.

I use a function with the HoldAll attribute to input the names of symbols from my package that contain all the content that the interface notebook needs to load. At run time I use the Initialization option of DynamicModule to load the package and set the notebook content.

If the DockedCells option is not specified in the notebook at build time then it doesn't display when I specify it with SetOptions (it will display if the user moves the window), so I specify a blank Cell at build time.

To prevent evaluating the Initialization option when CreateDocument builds the notebook, I first set the $FrontEnd option DynamicUpdating to False, save and close the notebook, and then reset it to Automatic.

The code for a minimal package that implements this is:

(* ::Package:: *)

BeginPackage["RunTimeGUIMinimal`"]

RunTimeGUIMinimal::usage = "RunTimeGUIMinimal[packages_List, titleFunction_Symbol, contentFunction_Symbol, tabFunction_Symbol]  "

GUITitle::usage = "GUITitle  "
GUIContent::usage = "GUIContent  "
GUIDockedCell::usage = "GUITabList  "


Begin["`Private`"]
(* Implementation of the package *)


SetAttributes[RunTimeGUIMinimal, HoldAll]


RunTimeGUIMinimal[packages_List, titleFunction_Symbol, contentFunction_Symbol, dockedCellFunction_Symbol] := 
    CreateDocument[{
        DynamicModule[{content},
            Dynamic[content[]],
            Initialization:>(
                Map[Needs, packages];

                (* set the WindowTitle *)
                SetOptions[EvaluationNotebook[], WindowTitle -> First@Flatten@{titleFunction}];

                (* set the DockedCells to their normal state *)
                SetOptions[EvaluationNotebook[], DockedCells -> dockedCellFunction];

                (* set the content *)
                content[] = contentFunction;
            ),
            UnsavedVariables:>{content}
        ]
        },
        (* set DockedCells to a blank Cell which will be replaced with SetOptions runs in Initialization. If this is not present, the DockedCells don't display until the user moves the window. *)
        DockedCells -> {Cell[BoxData[ToBoxes[""]], "DockedCell"]},
        Saveable -> False, 
        Selectable -> False, 
        ScreenStyleEnvironment -> "Working", 
        (* set the WindowTitle to the value of titleFunction at build time. This will be overwritten by the current package value at run time in the Initialization *)
        WindowTitle -> First@Flatten@{titleFunction}, 
        WindowElements -> {
            "VerticalScrollBar", 
            "HorizontalScrollBar", 
            "StatusArea"
        },
        CellContext->Notebook
    ]


GUITitle = "My Program"


GUIContent = TextCell["Some Dynamic content","Text"]


GUIDockedCell = Cell["A Docked Cell","Text"]


End[]

EndPackage[]

The commands to build the notebook are:

Needs["RunTimeGUIMinimal`"]

SetOptions[$FrontEnd, DynamicUpdating -> False]

nb = RunTimeGUIMinimal[{"RunTimeGUIMinimal`"}, GUITitle, GUIContent, 
   GUIDockedCell];

NotebookSave[nb, 
 FileNameJoin[{NotebookDirectory[], "RunTimeGUIMinimal"}]]

NotebookClose[nb]

SetOptions[$FrontEnd, DynamicUpdating -> Automatic]

NotebookOpen[
  FileNameJoin[{NotebookDirectory[], "RunTimeGUIMinimal.nb"}]];

Inspecting the notebook code shows that the functions remain unevaluated. The package functions GUITitle, GUIContent and GUIDockedCell can be modified and when the notebook is opened again (after a quit) it reflects the changes.

$\endgroup$
  • $\begingroup$ @Kuba Thanks for the cleanup. $\endgroup$ – David Creech Aug 24 '16 at 17:40
  • $\begingroup$ Since it is created only once you can use Refresh[..., None] in a docked cell. Or SingleEvaluation->True]. $\endgroup$ – Kuba Aug 24 '16 at 18:20
  • $\begingroup$ @Kuba Good idea, thanks. $\endgroup$ – David Creech Aug 24 '16 at 18:32

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.