Message Boards Message Boards

0
|
3694 Views
|
4 Replies
|
0 Total Likes
View groups...
Share
Share this post:

Correct way of developing an "application"

Posted 2 years ago

Let's say I build a sort of a tool to do some tasks done in my lab. It may be image processing of an image, data visualization of a data set etc.

The tool is composed of Wolfram's Control Objects: a browse button to load the file to be processed, some buttons, sliders, input fields and that sort of stuff, together with an image that updates as you move the sliders and check and uncheck the checkboxes, with all that residing in a palette. Just like an application to assist the physicist in his work.

Now I want to save this project and get it into a version control system (like git). I read here, that a decent WL programmer should "Get comfortable with packages".

On the other hand, I read in Paul Wellin's book "Essentials of Programming in Mathematica®", chapter 10, where he teaches about packages, that

Packages provide a framework to organize a collection of related functions that extend the functionality of Mathematica in some way.

That is, when you want to introduce a function of your own and share it with your collegues, than a .wl file is the right choice, together with the information you provide about this new function.

But this example I mentioned at the beginning of the post is not a typical package case.

And so my question is: What is the correct way of saving the code that generates this "application"?

Thanks a lot for any insight your have.

POSTED BY: Ehud Behar
4 Replies
Posted 2 years ago

Hi Ehud,

One way to have the convenience of working in a notebook and being able to generate a .m file with just the code is to add this line to the notebook

SetOptions[InputNotebook[], AutoGeneratedPackage -> Automatic]

and marking all Input cells that you want to be saved as Initialization cells. After evaluating the above, every time the notebook is saved, the .m file is regenerated. The .m file can be committed to GitHub and changes easily tracked. You just need to remember to mark any new Input cells as Initialization. There is probably a way to automate this.

Not sure how well it will work with your application. Try it out.

POSTED BY: Rohit Namjoshi

Ehud,

We do the same thing that Rohit suggests for our packages. You can execute his line of code or change the setting in Options Inspector in the Format menu.

We also use GIT to manage revisions. We include the generated m files and the regular notebooks and other docs in the repo (its not pretty because they are messy files but it makes it easy to pull complete versions -- you just need to control your git diff commands so you only look at .m files to see simple changes.)

One thing we did is we made a notebook called "RunMe.nb". When you pull the git repo, you execute the RunMe.nb file and it moves everything into the right place. Here is an example (slightly modified with bogus directory names) to show you how it works. We have some unrelated bin files and some stylesheets that we wanted to move as well (we gave them names starting with "StyleSheet"). We also compiled some unrelated code but I cut out that section of the RunMe.nb file. Beware: We terminate the kernel at the end so it does not affect anything we are doing. Our workflow is we make sure MMA is closed, pull the repo, run the RunMe.nb and get to work! The nice thing with this file is it works on anyones machine so if you pull a fresh copy of the repo, everything installs properly. I am noticing now that we wrote the code for Macintosh computers, but A Mac and Windows compatible version would require a minor edit to the way we assemble the directory paths (like adding OperatingSystem option to FileNameJoin, etc.)

PrintTemporary["Getting files to copy..."];
ackFilePaths = 
  FileNames[{"*.bin", "*.m"}, 
   StringJoin[NotebookDirectory[], #] & /@
    {"", "myDir1", 
     "myDir2", "myFunctions1"}];
styleSheetFiles = 
  FileNames[{"Stylesheet*.nb"}, NotebookDirectory[] <> "myFunctions1"];
PrintTemporary["Copying files to $UserBaseDirectory..."];
MapThread[
  CopyFile[#1, 
    FileNameJoin[{$UserBaseDirectory, "Applications/" <> #2}], 
    OverwriteTarget -> True] &,
  {ackFilePaths, Map[FileNameTake[#] &, ackFilePaths]}];
MapThread[
  CopyFile[#1, 
    FileNameJoin[{$UserBaseDirectory, 
      "SystemFiles/FrontEnd/StyleSheets/" <> #2}], 
    OverwriteTarget -> True] &, {styleSheetFiles, 
   Map[FileNameTake[#] &, styleSheetFiles]}];
PrintTemporary["Done"]; Pause[1];
Exit[];

Let me know if this is helpful.

Regards,

Neil

POSTED BY: Neil Singer
Posted 2 years ago

@Neil Singer and @Rohit Namjoshi, thanks a lot for your replies.

I prefer writing right from the start a .wl file. I am not giving up the notebook interface, it is just that it would serve me as a useful, interactive tool to quickly develop/test/modify expressions.

Let's say that my "application" is the following (taken from a previous question I asked on SE):

BeginPackage["ColorsIn3DPlot`"];


Begin["MyPrivate`"]


CreatePalette[Manipulate[
  Plot3D[Sin[x y], {x, 0, 3}, {y, 0, 3}, ColorFunction -> color,
   ImageSize -> {480, 480}],
  {color, {"Rainbow", "NeonColors", "BlueGreenYellow"}}],
 WindowFloating -> True,
 WindowSize -> All,
 WindowTitle -> "Nice Plot",
 Saveable -> False];


End[];


EndPackage[];

Is it considered as a good programming practice?

Is there a real need for the new context deceleration (Begin["MyPrivate"]`), or it rather should be omitted?

POSTED BY: Ehud Behar
Posted 2 years ago

A package usually defines functions that can be evaluated to do something. The package above will create the palette when it is loaded - usually not desirable. Also, the package has no public symbols, everything is private and cannot be accessed external to the package. A better structure might be

Quiet@Remove["ColorsIn3DPlot`*"];
BeginPackage["ColorsIn3DPlot`"];

(* plot3DPalette is exposed *)
plot3DPalette::usage = "Create a palette with a 3D plot";

Begin["Private`"];

(* Define any private symbols / functions that are needed *)

colors = {"Rainbow", "NeonColors", "BlueGreenYellow"};

plot3DPalette[size_List : {480, 480}] := 
  CreatePalette[
   Manipulate[
    Plot3D[Sin[x y], {x, 0, 3}, {y, 0, 3}, ColorFunction -> color, 
     ImageSize -> size], {{color, colors[[1]], "Colors"}, colors}], 
   WindowFloating -> True, WindowSize->All, 
   WindowTitle -> "Nice Plot", Saveable -> False];

End[];

EndPackage[];

?plot3DPalette

ColorsIn3DPlot`plot3DPalette[];

ColorsIn3DPlot`plot3DPalette[{200, 200}];
POSTED BY: Rohit Namjoshi
Reply to this discussion
Community posts can be styled and formatted using the Markdown syntax.
Reply Preview
Attachments
Remove
or Discard

Group Abstract Group Abstract